display player builds on DeadlockTracker.gg
// ==UserScript==
// @name Better DeadlockTracker.gg
// @namespace http://tampermonkey.net/
// @version 3
// @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://api.deadlock-api.com/v1/builds?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})`;
}
}
async function getHeroDetails(heroId) {
const item_details_url = "https://assets.deadlock-api.com/v2/heroes/";
const cacheKey = `hero_details_${heroId}`;
const cachedData = localStorage.getItem(cacheKey);
if (cachedData) {
return JSON.parse(cachedData);
}
try {
const response = await fetch(`${item_details_url}${heroId}`);
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 ${heroId}:`, error.message);
return `Unknown Item (${heroId})`;
}
}
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 = '';
build_for: for (const build of builds) {
const buildName = build.hero_build.name;
const buildDescription = build.hero_build.description;
const hero = await getHeroDetails(build.hero_build.hero_id);
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>
<a style="text-transform:uppercase;margin:0 5px;color:#a0afc1;">Build ID: ${ build.hero_build.hero_build_id }</a>
</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>
`;
}
buildHTML += `
</div>
`;
// Да, может быть чучут говнокод, НО
// я писал это всё без света и интернета,
// руководствуясь учебником по информатике 7 класс
// Вопросы?
const abilityOrder = build.hero_build.details?.ability_order?.currency_changes || [];
if (abilityOrder.length == 16) {
let table_sig_map = {};
table_sig_map[hero.items.signature1] = [];
table_sig_map[hero.items.signature2] = [];
table_sig_map[hero.items.signature3] = [];
table_sig_map[hero.items.signature4] = [];
buildHTML += `
<h2>${hero.name} Skill build</h2>
<div style="margin-bottom:5px;border:1px solid #ffffff22;margin:5 2.5px;padding:5px;width:calc(100% - 17px);" class="inner">
<div style="width:850px;">`;
for (let i = 0; i < abilityOrder.length; i++) {
const change = abilityOrder[i];
const change_ability_details = await getItemDetails(change.ability_id);
const { class_name } = change_ability_details;
if (!table_sig_map[class_name]) {
console.log("Че-то взорвалось из-за нового героя?");
console.log(build);
buildHTML += `</div></div>`;
continue build_for;
}
table_sig_map[class_name][0] = { image: change_ability_details.image };
for (const sig of Object.keys(table_sig_map)) {
if (class_name == sig) {
change_ability_details.currency_type = change.currency_type;
change_ability_details.delta = change.delta;
table_sig_map[class_name][i + 1] = change_ability_details;
}
}
}
for (const key of Object.keys(table_sig_map)) {
const sig_changes = table_sig_map[key];
buildHTML += `
<div style="height:50px;overflow:auto;">
<div style="background: #c6c2c7; margin: 3px; overflow: auto; float: left; border-radius: 5px; margin: 5px;width:40px;">
<img src="${ sig_changes[0].image }" style="filter: invert(1);width:40px;">
</div>
`;
for (let i = 1; i < 17; i++) {
if (!sig_changes[i]) {
buildHTML += `
<div style="float:left;width:46px;background:#ffffff22;margin:4 2px;border-radius:5px;height:40px;text-align:center;"></div>
`;
continue;
}
if (sig_changes[i].currency_type == 2) {
buildHTML += `
<div style="float:left;width:46px;background:#ffffff22;margin:4 2px;border-radius:5px;height:40px;text-align:center;">
<img src="/images/learn.png" style="margin:10 5px;">
</div>
`;
continue;
}
buildHTML += `
<div style="float:left;width:46px;background:#ffffff22;margin:4 2px;border-radius:5px;height:40px;text-align:center;">
<div style="font-weight:bold;font-size:15px;margin:10 5px;line-height:20px;">
<img src="/images/learn.png" style="filter: grayscale(1); float: left;">${-sig_changes[i].delta}
</div>
</div>
`;
}
buildHTML += `
</div>
`;
}
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>
</div>
`;
}
return buildHTML;
}
})();