// ==UserScript==
// @name BPTF Multi-tool
// @namespace https://steamcommunity.com/id/evangelions/
// @version 4.0.1
// @description QOL Features for TF2 trading!
// @author football.2014
// @match https://stntrading.eu/item/tf2/Unusual*
// @match https://backpack.tf/*
// @match https://steamcommunity.com/id/*/inventory/
// @match https://steamcommunity.com/profiles/*/inventory/
// @match https://scrap.tf/inventory
// @match https://scrap.tf/auctions/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=backpack.tf
// @run-at document-end
// @grant none
// @downloadURL https://github.com/durax-0xf/tf2-trader-extension/raw/main/tf2-trader.user.js
// ==/UserScript==
function format2STN(name, effect, quality) {
return `https://stntrading.eu/item/tf2/Unusual%20${effect}%20${encodeURI(name)}`;
}
function format2BPTF(name, effect, quality) {
return `https://backpack.tf/stats/Unusual/${encodeURI(name)}/Tradable/Craftable/${effect}`;
}
function format2MPTF(defIndex, effId) {
return `https://marketplace.tf/items/tf2/${defIndex};5;u${effId}`;
}
function createButton4STN() {
//document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-header > div.stats-header-item").childNodes[1].getAttribute("data-q_name")
//can be used to get the quality of the item on bptf
//defcat
//can sometimes be used to get the quality of the item on stn
//
//there could be a way to create pages for not only unusuals but any items (including skins)
function getBPTFLink(quality) {
const effect_and_name = itemData.itemName.replace("Unusual ","");
let target;
document.querySelector("body > div.d-flex.flex-column > div.bg-dark > div.m-auto > div > div > div.col-sm-8.col-md-7.col-lg-8 > div.row.g-0 > div:nth-child(1) > div.row.g-0 > div.col-12 > div").childNodes.forEach(e => {
if (e.style.color === "rgb(134, 80, 172)") {
target = e.innerText.slice(18).replace("\n", " ");
// unusual effect is always colored in rgb(134, 80, 172)
}
});
const itemName = effect_and_name.replace(target, "");
const effectId = document.querySelector("body > div.d-flex.flex-column > div.bg-dark > div.m-auto > div > div > div.col-sm-4.col-md-5.col-lg-4.p-3.h-100 > div > picture:nth-child(2) > source").srcset.split("/").at(-1).replace("@4x.webp", "");
return format2BPTF(itemName, effectId);
} // used to get the link to the item from stn to convert to bptf
function displayButtonBPTF(){
const div = document.querySelector("body > div.d-flex.flex-column > div.bg-dark > div.m-auto > div > div > div.col-sm-8.col-md-7.col-lg-8 > div.row.g-0 > div:nth-child(1) > div.px-3.px-sm-0.pb-2.d-flex.justify-content-between.justify-content-sm-start");
const btn = document.createElement('a');
btn.id = "bptf-link";
btn.className = "btn ms-sm-3 btn-secondary rounded-0";
btn.innerText = "Open on BPTF";
btn.href = getBPTFLink();
btn.target = "_blank";
const iconChild = document.createElement('img');
iconChild.src = "https://external-content.duckduckgo.com/ip3/backpack.tf.ico";
iconChild.style = "width: 18px; height: 18px; margin-right: 1px; display: inline-block;";
btn.appendChild(iconChild);
div.appendChild(btn);
}
displayButtonBPTF();
} // adds the button to open the item on bptf from stn as source
function createButton4BPTF() {
function displayButtonWiki() {
function wikiPages(node) {
const container = node.childNodes[2].childNodes[3];
const effectNameNode = Array.from(node.querySelectorAll('dd')).find(dd => dd.textContent.includes('Effect:'));
if (effectNameNode) {
const effectName = effectNameNode.textContent.slice(8).trim();
let hatNode = container.lastChild;
let unuNode = hatNode.cloneNode(true);
if (hatNode && hatNode.childNodes[1]) {
hatNode.childNodes[1].textContent = " Hat Wiki";
}
if (unuNode && unuNode.childNodes[1]) {
unuNode.childNodes[1].textContent = " Effect Wiki";
unuNode.href = `https://wiki.teamfortress.com/wiki/${effectName}`;
unuNode.target = "_blank";
container.appendChild(unuNode);
}
}
}
//observer for popover
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.matches('.popover[style*="display: block"]')) {
wikiPages(node);
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
function displayButtonSTN() {
function getSTNLink() {
const itemName = document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-header > div.stats-header-item > div.item.q-440-5.q-440-border-5").dataset.base_name;
const effectName = document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-header > div.stats-header-item > div.item.q-440-5.q-440-border-5").dataset.effect_name;
return format2STN(itemName, effectName);
}
const div = document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-subheader > div.price-boxes");
const btn = document.createElement('a');
btn.className = "price-box";
btn.target = "_blank";
btn.href = getSTNLink();
const iconChild = document.createElement('img');
iconChild.src = "https://external-content.duckduckgo.com/ip3/stntrading.eu.ico";
iconChild.style.cssText = "width: 32px; height: 32px;";
const textWrapper = document.createElement('div');
textWrapper.className = "text";
textWrapper.innerHTML = `
STN
Open on STN
`;
const iconWrapper = document.createElement('div');
iconWrapper.className = "icon";
iconWrapper.appendChild(iconChild);
btn.appendChild(iconWrapper);
btn.appendChild(textWrapper);
div.appendChild(btn);
}
function displayButtonMPTF() {
function getMPTFLink() {
const defIndex = document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-header > div.stats-header-item > div.item.q-440-5.q-440-border-5").attributes['data-defindex'].value;
const effId = document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-header > div.stats-header-item > div.item.q-440-5.q-440-border-5").attributes['data-effect_id'].value;
return format2MPTF(defIndex, effId);
}
const priceBoxes = document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-subheader > div.price-boxes");
const priceBox = document.createElement("a");
priceBox.className = "price-box";
priceBox.setAttribute("data-tip", "top");
priceBox.target = "_blank";
priceBox.title = "Marketplace.tf";
priceBox.href = getMPTFLink();
const mptfImg = document.createElement("img");
mptfImg.src = "/images/marketplace-medium.png?v=2";
priceBox.appendChild(mptfImg);
const textBox = document.createElement("div");
textBox.className = "text";
textBox.innerHTML = `
MP.TF
See past sales
`;
priceBox.appendChild(textBox);
priceBoxes.appendChild(priceBox);
}
function displayButtonsPrevNext(){
let root = document.querySelector("#page-content > div.row > div > div.stats-breadcrumbs");
let og = root.querySelector("a:nth-child(2)");
if (og) {
let ogEffID = Number(window.location.href.split("/")[8]);
let hrefBase = window.location.href.split("/");
const createNavLink = (text, offset) => {
let link = og.cloneNode();
hrefBase[8] = ogEffID + offset;
link.href = hrefBase.join("/");
link.innerText = text;
return link;
};
root.appendChild(createNavLink("🠜 Previous Effect", -1));
root.appendChild(createNavLink("Next Effect 🠞", 1));
}
}
displayButtonWiki();
if (/^\/stats\/Unusual\/[^\/]+\/Tradable\/Craftable\/[^\/]+$/.test(window.location.pathname)) {
displayButtonSTN();
displayButtonsPrevNext();
const priceBoxes = document.querySelector("#page-content > div.row > div > div.stats-body > div.stats-subheader > div.price-boxes").childNodes;
const hasMarketplaceTF = Array.from(priceBoxes).some(nd => nd.className === "price-box" && nd.title === "Marketplace.tf");
if (!hasMarketplaceTF) { // Only display the button if it doesn't exist
displayButtonMPTF();
}
}
}
// previous effect and next effect button functionality
function displayButtonsPrevNext(){
let root = document.querySelector("#page-content > div.row > div > div.stats-breadcrumbs");
let og = root.querySelector("a:nth-child(2)");
if (og) {
let ogEffID = Number(window.location.href.split("/")[8]);
let hrefBase = window.location.href.split("/");
const createNavLink = (text, offset) => {
let link = og.cloneNode();
hrefBase[8] = ogEffID + offset;
link.href = hrefBase.join("/");
link.innerText = text;
return link;
};
root.appendChild(createNavLink("🠜 Previous Effect", -1));
root.appendChild(createNavLink("Next Effect 🠞", 1));
}
}
// adds:
// 1. a button to open the item on stn from bptf as source
// 2. a button to open the item on mptf from bptf as source
// 3. buttons to open effect wiki page and unusual wiki page
function createButton4SCRAPTF() {
function selectAllSCRAPTF() {
const itemCont = document.querySelector("#user-bp-440 > div").children;
Array.from(itemCont).forEach(item => setTimeout(() => item.click()));
}
function createBptfLink() {
const auctionItem = document.querySelector("#main-container > div > div.well.auction-well > div:nth-child(6) > div.auction-items > div > div");
const hoverOver = document.querySelector(".hover-over");
const dataElement = document.querySelector("#main-container div.auction-items div div");
const data = dataElement ? [
dataElement.dataset.title.replace(/<[^>]+>/g, "").trim(),
dataElement.style.backgroundImage.match(/particles_440\/(\d+)_/)[1]
] : ["Not found", ""];
auctionItem.addEventListener('mouseenter', () => {
const cont = hoverOver.querySelector('.hover-over-content');
if (hoverOver.style.display !== 'none' && !cont.querySelector('.btn-bptf')) {
const bptfBtn = document.createElement('a');
bptfBtn.href = format2BPTF(data[0], data[1]);
bptfBtn.innerHTML = '';
bptfBtn.target = "_blank";
bptfBtn.classList.add('btn-bptf');
cont.appendChild(bptfBtn);
}
});
}
createBptfLink();
const btnCont = document.querySelector("#reverse-body > div.bp-txt");
const wAll = document.createElement('button');
wAll.className = "btn btn-embossed btn-primary btn-embossed btn-trade";
wAll.innerText = "SELECT ALL Items";
wAll.onclick = selectAllSCRAPTF;
btnCont.appendChild(wAll);
}
// adds:
// 1. a button to select all items in scrap.tf inventory
// 2. a button to open the item on bptf from scrap.tf as source
function displayPure4Steam() {
function createVisualCount(keys) {
const part = document.querySelector("#tabcontent_inventory > div.filter_ctn.inventory_filters");
const visual = document.createElement('div');
visual.className = "hover_item_name";
visual.innerText = `Total pure keys: ${keys}`;
part.appendChild(visual);
}
async function pureCount() {
if (!g_ActiveInventory) {
console.error("No active inventory found.");
return;
}
await g_ActiveInventory.LoadCompleteInventory(); // Ensure full inventory is loaded
const keyCount = Object.values(g_ActiveInventory.m_rgAssets)
.filter(item => item.description && item.description.market_hash_name === "Mann Co. Supply Crate Key")
.length;
return keyCount;
}
pureCount().then(keyCount => createVisualCount(keyCount));
}
// adds:
// 1. a visual count of pure keys in steam inventory
(function() {
'use strict';
if (window.location.hostname === 'stntrading.eu') {
createButton4STN();
} else if (window.location.hostname === 'backpack.tf') {
window.onload = createButton4BPTF;
} else if (window.location.hostname === 'scrap.tf') {
createButton4SCRAPTF();
} else if (/^https:\/\/steamcommunity\.com\/(id|profiles)\/([a-zA-Z0-9]+)\/inventory\/$/.test(window.location.href)) {
window.onload = displayPure4Steam;
}
})();