YATA - OC

Add NNB to the OC page

当前为 2023-10-12 提交的版本,查看 最新版本

// ==UserScript==
// @name         YATA - OC
// @namespace    yata.yt
// @version      0.4
// @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 display_player = (members, player) => {
    const urlParams = new URLSearchParams(player.children[0].children[0].href.split("?")[1]);
    if(urlParams.get("XID") in members["members"]) {
        const m = members["members"][urlParams.get("XID")];
        const lvl = player.children[1].innerText.trim()
        if(m.nnb_share) {
            player.children[1].innerHTML = `<b>#${m.crimes_rank}</b> / <b>${m.nnb}</b> / ${lvl}`
        } else {
            player.children[1].innerHTML = `<b>#${m.crimes_rank}</b> / <b title="Not shared">?</b> / ${lvl}`
            console.warn(`[yata - oc] ${m.name} NNB not shared on YATA`)
        }
    } else {
        player.children[1].innerHTML = `<b title="Not found"> NNB</b> / <b title="Not found"> NNB</b> / ${lvl}`
        console.warn(`[yata - oc] ${m.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#faction-crimes").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 => {
                display_player(members, player);
            });
        });
        div.querySelectorAll("ul.title li.level").forEach(t => {
            t.innerHTML = 'Rank / NNB / Level'
        });

        // 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 => {
                            display_player(members, player);
                        });
                    });
                    node.querySelectorAll("ul.title li.level").forEach(t => {
                        t.innerHTML = 'Rank / NNB / Level'
                    });
                });
            });
        }
        const observer = new MutationObserver(callback);
        observer.observe(div, { childList: true });


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