This commit is contained in:
Abhorrent_Anger 2024-03-07 16:13:33 +02:00
parent 4c5d5160d2
commit 02c36c06ca
5 changed files with 257 additions and 1 deletions

View File

@ -1,3 +1,3 @@
# BackpackDecalSpotter
Finds decaled items on Backpack.tf
Finds decaled items on Backpack.tf. Relies on legacy soon-to-be-deprecated endpoints. Should be hosted to avoid "Access to Image from origin 'null' has been blocked by CORS policy" errors when fetching accounts with decaled item listings.

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<html>
<head>
<title>Backpack Decal Spotter</title>
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="shortcut icon" href="favicon.ico">
<meta name="title" content="Backpack Decal Spotter">
<meta name="description" content="Finds decaled items on Backpack.tf">
<meta name="author" content="Abhorrent_Anger">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script src="script.js"></script>
</head>
<body>
<h4 id="empty-accounts" style='display:none'>No decaled items from </h4>
</body>
</html>

117
script.js Normal file
View File

@ -0,0 +1,117 @@
var checkable_accounts = { '76561198311319887': 'Weapon Crusher' };
decaled_objects = { 'Conscientious Objector': '3.66', 'Clan Pride': '2.11', 'Flair!': '4.22', 'Photo Badge': '2.44' }
backpack_page_limit = 3;
function fetchAccounts() {
var deferred = new $.Deferred();
for (const [item, price] of Object.entries(decaled_objects)) {
handleItem(item, price);
}
deferred.resolve(checkable_accounts);
return deferred.promise()
}
function handleItem(item, price) {
for (let i = 1; i <= backpack_page_limit; i++) {
$.ajax({
dataType: 'html',
url: 'https://backpack.tf/classifieds',
data: 'page=' + i + '&item=' + encodeURIComponent(item) + '&quality=6&tradable=1&craftable=1&australium=-1&killstreak_tier=0&numeric=price&comparison=lt&value=' + price + '&low=' + price,
async: false,
tryCount: 0,
retryLimit: 3,
success: function (msg) {
if ($('.col-md-6:first .alert', msg).length > 0) {
return false;
}
$('.col-md-6:first .user-link', msg).each(function () {
checkable_accounts[$(this).attr('data-id')] = $(this).attr('data-name');
});
},
error: function (response, status, error) {
if (response.status == 429) {
this.tryCount++;
if (this.tryCount <= this.retryLimit) {
setTimeout(() => {
$.ajax(this);
}, 3000);
return;
}
}
}
});
}
}
function handleAccounts() {
for (const [steamid64, name] of Object.entries(checkable_accounts)) {
handleAccount(steamid64, name);
}
}
function handleAccount(steamid64, name) {
var url = "https://backpack.tf/_inventory/" + steamid64;
$.ajax({
dataType: 'json',
url: url,
async: false,
tryCount: 0,
retryLimit: 3,
success: function (msg) { handleHTML(steamid64, name, msg['html']); },
error: function (response, status, error) {
if (response.status == 429) {
this.tryCount++;
if (this.tryCount <= this.retryLimit) {
setTimeout(() => {
$.ajax(this);
}, 3000);
return;
}
}
}
});
}
function handleHTML(steamid64, name, html_string) {
html = $.parseHTML(html_string);
var decaled_items = $('div.decal', html);
if (decaled_items.length == 0) {
var empty_accounts = $('#empty-accounts');
empty_accounts.append('<a href="https://next.backpack.tf/profiles/' + steamid64 + '">' + name + '</a>; ');
empty_accounts.fadeIn();
return false;
}
var inventory = $('<div class="inventory"></div>');
inventory.append('<a href="https://next.backpack.tf/profiles/' + steamid64 + '"><h3>' + name + '</h3></a>');
decaled_items.each(function () {
var url = "https://next.backpack.tf/item/" + $(this).parent().attr('data-id');
var a = $('<a href="' + url + '"></a>');
a.append($(this).parent());
inventory.append(a);
});
var inventories = $('.inventory');
if (inventories.length > 0) {
var child_count = inventory.children().length;
inventory.attr('data-items', child_count);
var last_div = null;
inventories.each(function () {
if (child_count > $(this).children().length) {
return false;
}
last_div = $(this);
});
if (last_div) {
last_div.after(inventory);
} else {
$('body').prepend(inventory);
}
} else {
$('#empty-accounts').before(inventory);
}
}
$(document).ready(function () {
$.when(fetchAccounts()).done(function () {
handleAccounts();
});
});

119
style.css Normal file
View File

@ -0,0 +1,119 @@
body {
background-color: #111;
color: #eee;
font-family: Verdana, Geneva, sans-serif;
font-size: 0.85em;
margin: 0px;
text-align: justify;
text-justify: inter-word;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
text-align: center;
}
div {
display: inline-block;
}
h3 {
text-align: center;
padding: 0.5em;
}
h4 {
color: #555;
text-align: center;
padding: 5em;
}
li {
display: inline-block;
width: 140px;
height: 140px;
position: relative;
border-radius: 3px;
margin: 10px;
border: 3px solid;
margin-bottom: 38px;
}
.decal {
width: 128px;
height: 128px;
margin: auto;
z-index: 2;
position: absolute;
top: 6px;
left: 6px;
}
.item-icon {
width: 24px;
height: 24px;
bottom: -38px;
left: -3px;
background-size: 24px;
z-index: 1;
position: absolute;
border-radius: 3px;
border: 3px solid;
}
.q-440-6,
.q-440-6 .item-icon,
.q-440-6 .bottom-right {
background-color: rgb(119, 101, 3);
border-color: rgb(181, 154, 5);
}
.q-440-11,
.q-440-11 .item-icon ,
.q-440-11 .bottom-right {
background-color: rgb(110, 56, 26);
border-color: rgb(144, 73, 34);
}
.nocraft {
border-style: dashed;
}
.top-left {
display: none;
}
.bottom-right {
position: absolute;
width: 105px;
height: 24px;
bottom: -38px;
left: 32px;
border-radius: 3px;
border: 3px solid;
margin: auto;
}
.bottom-right span {
text-align: center;
display: block;
margin-top: 3px;
}
a,
a:link,
a:visited,
a:hover,
a:active {
text-decoration: none;
color: #eee;
}
h4 a,
h4 a:link,
h4 a:visited,
h4 a:hover,
h4 a:active {
text-decoration: none;
color: #555;
font-style: italic;
}