Better DeadlockTracker.gg

display player builds on DeadlockTracker.gg

// ==UserScript==
// @name         Better DeadlockTracker.gg
// @namespace    http://tampermonkey.net/
// @version      2024-11-25
// @description  display player builds on DeadlockTracker.gg
// @author       erxson
// @match        https://deadlocktracker.gg/players*
// @match        https://deadlocktracker.gg/player/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=deadlocktracker.gg
// @grant        none
// ==/UserScript==

(async function() {
    var builds = [];

    if (window.location.pathname.startsWith("/player/")) {
        const nicknameDiv = document.querySelector(
            "body > div > div > div:nth-child(3) > div:nth-child(1) > div"
        );
        const playerLink = nicknameDiv.querySelector("a");
        if (playerLink) {
            const steamId = playerLink.getAttribute("href").replace("/player/", "");
            if (!window.location.pathname.includes(`/player/${steamId}/`)) {
                builds = await getBuilds(steamId);
                let buildsHTML = await formatPlayerBuilds();

                const button = document.createElement("a");
                button.textContent = "BUILDS";
                button.href = "#";
                button.style =
                "float:left;font-weight:bold;width:70px;padding:0 10px;text-align:center;";
                button.onclick = async () => {
                    const output_element = document.querySelector("body > div > div > div:nth-child(3) > div.profile_left");
                    output_element.innerHTML = buildsHTML;
                };
                if (builds) {
                    document.querySelector("body > div > div > div:nth-child(3) > div:nth-child(3)").appendChild(button);
                }
            }
        }
    }
    async function getBuilds(steam_id) {
        const builds_url = `https://data.deadlock-api.com/v1/builds/by-author-id/${steam_id}?limit=100&only_latest=true&sort_by=updated_at&sort_direction=desc`;
        try {
            const response = await fetch(builds_url);
            if (!response.ok) {
                console.error(`Blyat! Status: ${response.status}`);
                return null;
            }
            const data = await response.json();
            return data;
        } catch (error) {
            console.error("Blyat:", error.message);
            return null;
        }
    }

    async function getItemDetails(itemId) {
        const item_details_url = "https://assets.deadlock-api.com/v2/items/";
        const cacheKey = `item_details_${itemId}`;
        
        const cachedData = localStorage.getItem(cacheKey);
        if (cachedData) {
            return JSON.parse(cachedData);
        }

        try {
            const response = await fetch(`${item_details_url}${itemId}`);
            if (!response.ok) {
                throw new Error(`HTTP error! Status: ${response.status}`);
            }
            const data = await response.json();
            
            localStorage.setItem(cacheKey, JSON.stringify(data));
            setTimeout(() => localStorage.removeItem(cacheKey), 3600000*48);
            
            return data;
        } catch (error) {
            console.error(`Ошибка при получении информации о предмете с ID ${itemId}:`, error.message);
            return `Unknown Item (${itemId})`;
        }
    }

    function timeAgo(timestamp) {
        const now = new Date();
        const diffInHours = Math.floor((now - new Date(timestamp * 1000)) / (1000 * 60 * 60));
        const diffInDays = Math.floor(diffInHours / 24);
    
        if (diffInDays > 0) {
            return `${diffInDays} day${diffInDays > 1 ? 's' : ''} ago`;
        } else if (diffInHours > 0) {
            return `${diffInHours} hour${diffInHours > 1 ? 's' : ''} ago`;
        } else {
            return "just now";
        }
    }

    async function formatPlayerBuilds() {
        let buildHTML = '';

        for (const build of builds) {
            const buildName = build.hero_build.name;
            const buildDescription = build.hero_build.description || "Отсутствует";
            const categories = build.hero_build.details?.mod_categories || [];

            buildHTML += `
                <div style="margin:5 0px;overflow:auto;2width:870px;border-radius:5px;2background:#23231e;padding:0 5px;width:calc(100% - 12px);padding-bottom:2px;margin-bottom:7px;" class="holder">
                    <a style="float:left;">
                        <img src="/images/heroes/${ build.hero_build.hero_id }.png" style="height:35px;float:left;margin:7 0 5 3px;background:#ffffff11;border-radius:2px;">
                    </a>
                    <div style="float:left;">
                        <div style="overflow:auto;">
                            <a style="float:left;">
                                <h1 style="    color: #ffefd7;margin:0 5px;overflow:auto;font-size: 13px; font-weight: bold; font-weight: bold; text-transform: uppercase; margin: 0px; padding: 0px; margin: 8 5 0 0px !important; padding: 0 5px;float:left;">${ buildName }</h1>
                            </a>
                            <div style="color:#ffde00;overflow:auto;float:left;font-weight:bold;font-size:11px;background: #ffd7004f; border-radius: 5px; padding: 1 4px;margin-left:5px;    margin: 8px 0 0 0px;">
                                <img src="/images/star.png" style="width:10px;margin:2px;float:left;">${ build.num_favorites }
                            </div>
                            <div style="font-size:11px;margin-left:5px;color: #a0afc1;float:left;text-transform: uppercase;font-weight: bold;padding-top:9px;">${ timeAgo(build.hero_build.last_updated_timestamp) }</div>
                        </div>
                    </div>
            `;

            buildHTML += `
                    <div style="margin-bottom:5px;border:1px solid #ffffff22;margin:5 2.5px;padding:5px;width:calc(100% - 17px);" class="inner">
            `;
            for (const category of categories) {
                buildHTML += `
                        <div style="padding:3 0px;">
                            <div style="color: #a0afc1;margin-left:0px;line-height:18px;">
                                <h2 style="font-weight:bold;text-transform:uppercase;font-size:11px;margin-left:5px;font-weight:bold;color: #d7e9ff;font-weight:bold;text-transform:uppercase;font-size:11px;margin-left:5px;font-weight:bold;color: #d7e9ff;margin: 0px !important;">${category.name}</h2>

                                ${ category.description ? `
                                <div style="color:#aaa;margin-left:5px;text-indent: 0px">
                                    <div style="float:left;width:5px;height:5px;margin:6 5px;background:#ffefd7;"></div>` +
                                    (category.description.split(". ").filter(item => item !== "").length > 1
                                        ? category.description.split(". ").join('<br><div style="float:left;width:5px;height:5px;margin:5px;background:#ffefd7;"></div>')
                                        : category.description) +
                                '</div>' : '' }
                            </div>
                            <div style="overflow:auto;margin-left:10px;">
                `;
                const mods = category.mods || [];
                for (const mod of mods) {
                    const item = await getItemDetails(mod.ability_id);
                    let item_color = "";
                    switch (item.item_slot_type) {
                        case "weapon":
                            item_color = "#dc8e21";
                            break;
                        case "vitality":
                            item_color = "#c7ee8e";
                            break;
                        case "spirit":
                            item_color = "#c288f0";
                            break;
                        default:
                            item_color = "#fff";
                    }
                    buildHTML += `
                                <a href="/items/${ item.name.toLowerCase().replace(" ", "-") }" title="${ item.name }">
                                    <div style="display:inline-block;border-radius:5px;border-radius:5 5 0 0px;background:#00000033;background:${ item_color };2width:180px;text-align:center;float:left;margin:4px;position:relative;2height:40px;" class="tooltip-container">
                                        <img src="${ item.image }" style="width:30px;margin:0 4 0 15px;2margin:auto;filter: invert(1);float:left;">
                                        <div style="position:absolute;top:-2px;left:3px;width:100%;text-align:left;color:#fff;font-size:15px;font-weight:bold;" class="shad">I</div>
                                        <div style="position:absolute;bottom:0px;left:3px;width:100%;text-align:left;color:#fff;font-size:8px;color:gold;font-weight:bold;line-height:10px;" class="shad">${ item.cost }</div>
                                    </div>
                                </a>
                `;
                }
                buildHTML += `
                            </div>
                        </div>
                `;
            }

            if (buildDescription) {
                buildHTML += `
                        <h2>Build Description</h2>
                        <div style="margin-bottom:5px;border:1px solid #ffffff22;margin:5 2.5px;padding:5px;width:calc(100% - 17px);" class="inner">
                            ${buildDescription}
                        </div>
                `;
            }

            buildHTML += `
                </div>
            `;
        }

        return buildHTML;
    }
})();