YATA - OC

Add NNB to the OC page

目前為 2023-10-12 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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');
        }
    });
});