YATA - OC

Add NNB to the OC page

As of 2023-10-12. See the latest version.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         YATA - OC
// @namespace    yata.yt
// @version      0.3
// @grant        GM_addStyle
// @description  Add NNB to the OC page
// @author       Kivou [2000607]
// @grant        GM.xmlHttpRequest
// @match        https://www.torn.com/factions.php*
// @match        https://www.torn.com/preferences.php*
// @icon         https://yata.yt/media/yata-small.png
// @run-at       document-end
// @license      WTFPL
// ==/UserScript==

// Copyright © 2023 Kivou [2000607] <[email protected]>
// This work is free. You can redistribute it and/or modify it under the
// terms of the Do What The Fuck You Want To Public License, Version 2,
// as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.

// ---------------- //
// HELPER FUNCTIONS //
// ---------------- //

const waitForElement = (target, selector) => {
    return new Promise(resolve => {
        if (target.querySelector(selector)) {
            return resolve(target.querySelector(selector));
        }
        const observer = new MutationObserver(mutations => {
            if (target.querySelector(selector)) {
                resolve(target.querySelector(selector));
                observer.disconnect();
            }
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
};

const gmGet = async (url) => {
    console.log(`[kivou - gmGet] ${url}`)

    return new Promise((resolve, reject) => {
        GM.xmlHttpRequest({
            url,
            method: "GET",
            onload: (response) => {
                resolve(new Response(response.response, { statusText: response.statusText, status: response.status }))
            },
            onerror: (error) => {
                reject(error)
            }
        });
    })
        .catch((error) => {
            throw { message: "critical error", code: error.status }
        })
        .then((response) => {
            const result = response.json()
            return result.then((body) => {
                if (typeof body.error == 'undefined') {
                    return body
                } else {
                    throw {message: body.error.error, code: body.error.code}
                }
            })
        })
}

const add_nnb = (members, player) => {
    const urlParams = new URLSearchParams(player.children[0].children[0].href.split("?")[1]);
    if(urlParams.get("XID") in members["members"]) {
        const member = members["members"][urlParams.get("XID")];
        if(member.nnb_share) {
            const n = members["members"][urlParams.get("XID")].nnb
            player.children[1].innerHTML += ` / <b>${n}</b> NNB`
            console.log(`[yata - oc] ${member.name} ${n} NNB`)
        } else {
            player.children[1].innerHTML += ' / <b title="Not shared">?</b> NNB'
            console.warn(`[yata - oc] ${member.name} NNB not shared on YATA`)
        }
    } else {
        player.children[1].innerHTML += ' / <b title="Not found"> NNB</b>'
        console.warn(`[yata - oc] ${member.name} not found in the database`)
    }
}

// ------------- //
// SETUP API KEY //
// ------------- //

waitForElement(document, "div.preferences-container").then(div => {

    let injected = false;

    // triggered by clicking on crimes tab
    const callback = (mutations, observer) => {
        [...mutations].forEach(mutation => {
            [...mutation.addedNodes].filter(n => n.className && n.className.includes("keyRow___")).forEach(node => {
                // const name = node.querySelector("p[id^=key-name]").innerText
                const key_node = node.querySelector("input");
                if(!localStorage.getItem('key')) {
                    localStorage.setItem('key', key_node.value);
                    document.getElementById("yata-api-key-oc").innerHTML = localStorage.getItem('key')
                }
                key_node.style.cursor = "pointer"
            });
        });
        if(!injected) {
            const modal = document.createElement("div");
            modal.innerHTML = `<div class="content-title">`
            modal.innerHTML += `<b>[YATA - OC]</b> API key <span id="yata-api-key-oc" style="font-family: monospace; font-weight: bold;">${localStorage.getItem('key')}</span>`
            modal.innerHTML += ` | Click <a href="/preferences.php#tab=api" class="t-blue">on a key</a> to change`
            modal.innerHTML += ` | Go back to <a href="/factions.php?step=your#/tab=crimes" class="t-blue">organized crimes</a>`
            modal.innerHTML += ` | <span id="yata-api-key-oc-rm" class="t-blue" style="cursor: pointer;">Remove</span> your key`
            modal.innerHTML += `</div>`
            modal.innerHTML += `<div class="clear"></div>`
            modal.innerHTML += `<hr class="page-head-delimiter m-top10">`
            div.insertAdjacentElement('beforebegin', modal);
            injected = true;
        }
    }
    const observer = new MutationObserver(callback);
    observer.observe(div, { childList: true, subtree: true });

    document.querySelector("div.content-wrapper").addEventListener('click', e => {
        const button = e.target;
        if(button.tagName == 'INPUT' && button.id.includes('key-row')) {
            localStorage.setItem('key', button.value);
            document.getElementById("yata-api-key-oc").innerHTML = `${localStorage.getItem('key')}`
        }
        else if(button.tagName == 'SPAN' && button.id == 'yata-api-key-oc-rm') {
            localStorage.removeItem('key');
            document.getElementById("yata-api-key-oc").innerHTML = `${localStorage.getItem('key')}`
        }
    })

});

// ----------- //
// FILL UP NNB //
// ----------- //

waitForElement(document, "div#factions").then(div => {

    API_KEY = localStorage.getItem('key')

    if(!API_KEY) {
        const modal = document.createElement("div");
        modal.innerHTML = `<hr class="page-head-delimiter">`
        modal.innerHTML += `<div class="m-top10">`
        modal.innerHTML += `<b>[YATA - OC]</b> <b style="color: var(--default-red-color)">API key not found</b>`
        modal.innerHTML += ` | Visit <a href="/preferences.php#tab=api" class="t-blue">preferences</a> to set it up`
        modal.innerHTML += `</div>`
        modal.innerHTML += `<div class="clear"></div>`
        div.insertAdjacentElement('afterend', modal);
        return
    }

    const modal = document.createElement("div");
    modal.innerHTML = `<hr class="page-head-delimiter">`
    modal.innerHTML += `<div class="m-top10">`
    modal.innerHTML += `<b>[YATA - OC]</b> <b style="color: var(--default-green-color)">Filling NNB</b>`
    modal.innerHTML += ` | Visit <a href="/preferences.php#tab=api" class="t-blue">preferences</a> to change your key`
    modal.innerHTML += `</div>`
    modal.innerHTML += `<div class="clear"></div>`
    div.insertAdjacentElement('afterend', modal);

    gmGet(`https://yata.yt/api/v1/faction/members/?key=${API_KEY}`).then(members => {

        // triggered if directly landing on crimes
        div.querySelectorAll("ul.details-list, ul.plans-list").forEach(ul => {
            ul.querySelectorAll("ul.item").forEach(player => {
                add_nnb(members, player);
            });
        });

        // triggered by clicking on crimes tab
        const callback = (mutations, observer) => {
            [...mutations].forEach(mutation => {
                [...mutation.addedNodes].filter(n => n.className && n.className.includes("faction-crimes-wrap")).forEach(node => {
                    node.querySelectorAll("ul.details-list, ul.plans-list").forEach(ul => {
                        ul.querySelectorAll("ul.item").forEach(player => {
                            add_nnb(members, player);
                        });
                    });
                });
            });
        }
        const observer = new MutationObserver(callback);
        observer.observe(div, { childList: true });


    }).catch(error => {
        alert(`[yata - oc] ${error.message}`);
        if(error.code == 4) {
            localStorage.removeItem('key');
        }
    });
});