- // ==UserScript==
- // @name Steam, Card sets viewer
- // @name:ja Steam, Card sets viewer
- // @namespace http://tampermonkey.net/
- // @version 1.1.5
- // @description Happy trading 1:1 card sets
- // @description:ja Happy trading 1:1 card sets
- // @author You
- // @require http://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
- // @match https://steamcommunity.com/tradeoffer/*
- // @grant GM.xmlHttpRequest
- // @run-at document-end
- // @nowrap
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- var $ = jQuery.noConflict();
- var GetBadgeInformationUrl = "https://www.steamcardexchange.net/api/request.php?GetBadgePrices_Member";
- var GetInventoryUrl = `https://www.steamcardexchange.net/api/request.php?GetInventory&_=${new Date().getTime()}`;
- var StorageKey = "SCE_Badges";
- var ButtonClass = "btn_darkblue_white_innerfade btn_small new_trade_offer_btn";
- var MaxBadgeLevel = 5;
-
- function escapeHtml (string) {
- if(typeof string !== 'string') {
- return string;
- }
- return string.replace(/[&'`"<>]/g, function(match) {
- return {
- '&': '&',
- "'": ''',
- '`': '`',
- '"': '"',
- '<': '<',
- '>': '>',
- }[match]
- });
- }
-
- function createSetsObjectFromInventory (user) {
- var sets = {};
- var rgInventory = user.rgContexts[753][6].inventory.rgInventory;
- for (var instanceid in rgInventory) {
- var item = rgInventory[instanceid];
-
- // Check whether item type is card
- var isCard = false;
- var isNormal = false;
- for (var i = 0; i < item.tags.length; i++) {
- // item_class_2 is type of trading card
- if (item.tags[i].category == "item_class" &&
- item.tags[i].internal_name == "item_class_2") {
- isCard = true;
- }
- // cardborder_0 is type of normal card
- if (item.tags[i].category == "cardborder" &&
- item.tags[i].internal_name == "cardborder_0") {
- isNormal = true;
- }
- }
-
- if (!isCard) continue;
- if (!isNormal) continue;
-
- if (!sets[item.market_fee_app]) {
- sets[item.market_fee_app] = {
- appId: item.market_fee_app,
- cardsInSet: -1,
- items: {}
- };
- }
- if (!sets[item.market_fee_app].items[item.market_hash_name]) {
- sets[item.market_fee_app].items[item.market_hash_name] = {
- hash: item.market_hash_name,
- quantity: 1,
- instances: [instanceid],
- };
- } else {
- sets[item.market_fee_app].items[item.market_hash_name].quantity++;
- sets[item.market_fee_app].items[item.market_hash_name].instances.push(instanceid);
- }
- }
-
- return sets;
- }
-
- function isValidSteamInventory() {
- var errorUser;
- function checkIsLoaded(user) {
- if (!user) throw "Error: Not found {0} user object".replace("{0}", errorUser);
- var inv = user.rgContexts[753][6].inventory;
- if (!inv) throw "Error: {0} Inventory is not found".replace("{0}", errorUser);
- if (!inv.initialized) throw "Error: {0} Inventory is unloaded".replace("{0}", errorUser);
- if (inv.appid != "753") throw "Error: {0} Inventory isn't Steam Inventory".replace("{0}", errorUser);
- if (!inv.rgInventory) throw "Error: {0} rgInventory is unloaded".replace("{0}", errorUser);
- }
-
- errorUser = "Your";
- checkIsLoaded(UserYou);
- errorUser = "Partners";
- checkIsLoaded(UserThem);
-
- console.log("SCE: Both Inventory are loaded");
- return true;
- }
-
- function loadBadgeInformation() {
- return new Promise(function (resolve, reject) {
- GM.xmlHttpRequest({
- url: GetBadgeInformationUrl,
- method: "GET",
- onerror: function () {
- reject("Couldn't get badge information. You need to log in to steamcardexchange.net.");
- },
- onload: function (xhr) {
- var badges = {}, data;
- try {
- var parsedJSON = JSON.parse(xhr.responseText);
- for (var i = 0; i < parsedJSON.data.length; i++) {
- data = parsedJSON.data[i];
- badges[data[0][0]] = {
- title: data[0][1].trim(),
- cardsInSet: data[1],
- badgeValue: data[2],
- yourLevel: parseInt(data[3]),
- };
- }
- } catch (error) {
- console.log(error, xhr, data);
- reject(error);
- return;
- }
- resolve(badges);
- }
- });
- });
- }
-
- function loadSCEInventory() {
- return new Promise(function(resolve, reject) {
- GM.xmlHttpRequest({
- url: GetInventoryUrl,
- method: "GET",
- onerror: function() {
- reject("Couldn't get SCE inventory. You need to log in to steamcardexchange.net.");
- },
- onload: function (xhr) {
- var inventory = {}, data;
- try {
- var parsedJSON = JSON.parse(xhr.responseText);
- for (var i = 0; i < parsedJSON.data.length; i++) {
- data = parsedJSON.data[i];
- inventory[data[0][0]] = {
- title: data[0][1].trim(),
- cardsInSet: data[3][0],
- };
- }
- } catch (error) {
- console.log("SCE:", error, xhr);
- reject(error);
- }
-
- resolve(inventory);
- }
- });
- });
- }
-
- function applyBadgeInformationToSetsObject(badges, sets, isSelfInventory, isExtraOnly) {
- var fee, set;
-
- // Add badge information to sets variable
- for (fee in badges) {
- set = sets[fee];
- if (!set) continue;
-
- sets[fee] = $.extend(true, {
- yourLevel: 0,
- fullSetQuantity: 0,
- hasFullSet: false,
- }, set, badges[fee]);
- }
-
- // Count complete card sets
- for (fee in sets) {
- set = sets[fee];
-
- var totalCards = 0;
- var cardsCount = 0;
- var minQty = Number.MAX_VALUE;
-
- for (var hash in set.items) {
- var item = set.items[hash];
- minQty = Math.min(minQty, item.quantity);
- cardsCount++;
- totalCards += item.quantity;
- }
- set.totalCards = totalCards;
- if (set.cardsInSet > 0 && set.cardsInSet == cardsCount) {
- set.hasFullSet = true;
- set.fullSetQuantity = minQty;
- if (isSelfInventory) {
- var extraQuantity = set.fullSetQuantity - (MaxBadgeLevel - set.yourLevel);
- set.extraQuantity = extraQuantity > 0 ? extraQuantity : 0;
- set.necessaryQuantity = 0;
- } else {
- set.extraQuantity = 0;
- set.necessaryQuantity = Math.min(MaxBadgeLevel - set.yourLevel, set.fullSetQuantity);
- }
- } else {
- set.hasFullSet = false;
- set.fullSetQuantity = 0;
- set.extraQuantity = 0;
- set.necessaryQuantity = 0;
- }
- }
-
- var displayList = [];
- for (fee in sets) {
- set = sets[fee];
- if (!set.hasFullSet) continue;
- if (isExtraOnly) {
- if (isSelfInventory && set.extraQuantity <= 0) continue;
- if (!isSelfInventory && set.necessaryQuantity <= 0) continue;
- }
-
- displayList.push(set);
- }
-
- // sort by title
- displayList.sort(function (a, b) {
- return a.title > b.title ? 1 : -1;
- });
-
- return displayList;
- }
-
- function buildList(displayList, isYourInventory, isExtraOnly) {
- var set, fee;
-
- var textBuilder = "";
- var markdownBuilder = "";
- var htmlBuilder = "";
- var steamBuilder = "";
-
- for (var k = 0; k < displayList.length; k++) {
- set = displayList[k];
- var quantity = set.fullSetQuantity;
- if (isExtraOnly) {
- quantity = isYourInventory ? set.extraQuantity : set.necessaryQuantity;
- }
-
- var yourBadgeUrl = `${UserYou.strProfileURL}/gamecards/${set.appId}/`;
- var theirBadgeUrl = `${UserThem.strProfileURL}/gamecards/${set.appId}/`;
- var perValue = set.badgeValue ? "$" + Math.round(parseFloat(set.badgeValue.replace("$", "")) / set.cardsInSet * 1000) / 1000 : null;
- var replacedTitle = set.title.replace("[", "[").replace("]", "]");
- var classList = "set";
- if (set.extraQuantity > 0) {
- classList += " extra";
- }
- if (set.necessaryQuantity > 0) {
- classList += " necessary";
- }
- classList += set.badgeValue ? " marketable" : " non-marketable";
-
- // Add content as text to pre tag so don't need to html-escape
- textBuilder += `<span class="${classList}">${quantity}x ${replacedTitle}</span>`;
- // Add content as text to pre tag so don't need to html-escape
- // but need to escape charactors that is used by markdown
- markdownBuilder += `<span class="${classList}">${quantity}x [${replacedTitle}](${yourBadgeUrl}) (${set.cardsInSet})</span>`;
- // Add content as text to pre tag so don't need to html-escape
- // but need to escape charactors that is used by steam code
- steamBuilder += `<span class="${classList}">${quantity}x [url=${yourBadgeUrl}]${replacedTitle}[/url]</span>`;
- // Append content as html to body so need to html-escape variables
-
- var htmlPart = `<div>
- <button data-fee='${set.appId}' data-count=1 class='AddSetToTradeButton'>Add</button>
- <span class="${classList}">${quantity}x <a href='${yourBadgeUrl}' target='_blank'>${escapeHtml(set.title)}</a> (<a href='${theirBadgeUrl}' target='_blank'>partners</a>)
- ${set.cardsInSet} ${set.badgeValue ? `(${perValue} / ${set.badgeValue})` : ""}</span>
- </div>`;
-
- htmlBuilder += htmlPart;
- }
-
- return $("<div />")
- .append($("<div />").addClass("SetListText").append(textBuilder))
- .append($("<div />").addClass("SetListMarkdown").append(markdownBuilder))
- .append($("<div />").addClass("SetListSteamCode").append(steamBuilder))
- .append($("<div />").addClass("SetListHtml").append(htmlBuilder));
- }
-
- async function main() {
- console.log("main()");
- var isExtraOnly = $("#DisplayExtraOnlyCheckbox")[0].checked;
-
- var yours = createSetsObjectFromInventory(UserYou);
- var theirs = createSetsObjectFromInventory(UserThem);
-
- // console.log("Users:", yours, theirs);
-
- var badges, inventory;
- try {
- badges = JSON.parse(localStorage[StorageKey]);
- } catch (error) {
- badges = null;
- }
-
- if (!badges) {
- try {
- badges = await loadBadgeInformation();
- inventory = await loadSCEInventory();
- console.log(badges, inventory);
-
- badges = $.extend(true, badges, inventory);
- localStorage[StorageKey] = JSON.stringify(badges);
- } catch (error) {
- alert(error);
- return;
- }
- }
-
- // console.log("Badges:", badges);
-
- if (!badges) return;
-
- var yourList = applyBadgeInformationToSetsObject(badges, yours, true, isExtraOnly);
- var theirList = applyBadgeInformationToSetsObject(badges, theirs, false, isExtraOnly);
-
- console.log("Your list:", yourList, yours);
- console.log("Their list:", theirList, theirs);
-
- var $yourList = buildList(yourList, true, isExtraOnly);
- var $theirList = buildList(theirList, false, isExtraOnly);
-
- // console.log("$DisplayList:", $yourList, $theirList);
-
- $("#SetListContainer, .CardsInSet").remove();
- $(`<div id="SetListContainer" display-type="Html" />`)
- .append("<div><a class='SwitchSetList'>Html</a><a class='SwitchSetList'>Text</a><a class='SwitchSetList'>Markdown</a><a class='SwitchSetList'>SteamCode</a><a class='CloseSetList'>Close</a></div>")
- .append($yourList.attr({ id: "YoursDisplayList" }))
- .append($theirList.attr({ id: "TheirsDisplayList" }))
- .appendTo("body");
-
- $(".CloseSetList").click(function (ev){
- ev.preventDefault();
- ev.stopPropagation();
-
- $("#SetListContainer").remove();
-
- $("#trade_area .item").each(function () {
- $(`<div class="CardsInSet" />`)
- .text(badges[this.rgItem.market_fee_app].cardsInSet)
- .appendTo(this.rgItem.element);
- });
- });
-
- $(".SwitchSetList").click(function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- $("#SetListContainer").attr("display-type", $(this).text());
- });
-
- $(".AddSetToTradeButton").click(function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- var isSelfInventory = $(this).parents("#YoursDisplayList").length == 1;
- var fee = $(this).attr("data-fee");
- var count = $(this).attr("data-count");
- var targetSet = isSelfInventory ? yours[fee] : theirs[fee];
- var targets = [];
- for (var hash in targetSet.items) {
- var instances = targetSet.items[hash].instances;
- var addables = [];
- for (var i = 0; i < instances.length; i++) {
- var $c = $((isSelfInventory ? "#your_slots" : "#their_slots") + " #item753_6_" + instances[i]);
- if ($c.length == 0) {
- addables.push(instances[i]);
- if (addables.length == count) {
- break;
- }
- }
- }
- if (addables.length != count) {
- alert("Cards aren't enough to add complete set");
- return;
- }
- for (var j = 0; j < addables.length; j++) {
- targets.push(addables[j]);
- }
- }
- for (var n = 0; n < targets.length; n++) {
- MoveItemToTrade($("#item753_6_" + targets[n])[0]);
- }
- });
- }
-
- var $controllerContainer = $(`<div id="csv-area"><div class="header">Steam, Card sets viewer</div></div>`).appendTo("#inventory_box");
-
- $("<button />")
- .append("<span>List card sets</span>")
- .addClass(ButtonClass)
- .click(function () {
- try {
- if (isValidSteamInventory()){
- main();
- }
- } catch (error) {
- alert(error);
- }
- })
- .appendTo($controllerContainer);
-
- $("<button />")
- .append("<span>Clear cache</span>")
- .addClass(ButtonClass)
- .click(() => delete localStorage[StorageKey])
- .appendTo($controllerContainer);
-
- $(`<input type="checkbox" id="DisplayExtraOnlyCheckbox" />`).appendTo($controllerContainer);
- $(`<label for="DisplayExtraOnlyCheckbox" />`).text("Extra/Necessary only").appendTo($controllerContainer);
-
- $("<style />").text(`
- #SetListContainer .SetListText,
- #SetListContainer .SetListMarkdown,
- #SetListContainer .SetListSteamCode,
- #SetListContainer .SetListHtml {
- display: none;
- }
-
- #SetListContainer[display-type=Text] .SetListText,
- #SetListContainer[display-type=Markdown] .SetListMarkdown,
- #SetListContainer[display-type=SteamCode] .SetListSteamCode,
- #SetListContainer[display-type=Html] .SetListHtml {
- display: block;
- }
-
- #SetListContainer {
- position: fixed;
- z-index: 10000;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background: #000000dd;
- overflow-y: scroll;
- padding: 24px 40px;
- }
- #SetListContainer pre {
- white-space: pre-wrap;
- word-break: break-all;
- }
- #SetListContainer > div {
- margin-bottom: 24px;
- padding-top: 12px;
- }
- #YoursDisplayList, #TheirsDisplayList {
- position: relative;
- width: 48%;
- float: left;
- }
- #YoursDisplayList::before, #TheirsDisplayList::before {
- display:block;
- position: absolute;
- top: -20px;
- font-size: 51px;
- color: #ff74;
- z-index: -1;
- }
- #YoursDisplayList::before {
- content: "Your's";
- }
- #TheirsDisplayList::before {
- content: "Partner's";
- }
- #TheirsDisplayList::before {
- display: block;
- break: all;
- content: "",
- }
- .AddSetToTradeButton {
- padding: 0 3px;
- }
- .AddSetToTradeButton:disabled {
- opacity: 0.1;
- }
- .SwitchSetList {
- margin-right: 8px;
- }
- .CardsInSet {
- position: absolute;
- font-size: 24px;
- color: #ff7a;
- z-index: 100;
- pointer-events: none;
- top: 0;
- left: 0;
- text-shadow: 1px 1px #000;
- }
- #SetListContainer .SetListText .set,
- #SetListContainer .SetListMarkdown .set,
- #SetListContainer .SetListSteamCode .set {
- display: block;
- white-space: pre;
- }
- #SetListContainer .set.necessary {
- color: yellow;
- }
- #SetListContainer .set.extra {
- color: lime;
- }
- #SetListContainer .set.non-marketable {
- font-weight: bold;
- }
- #csv-area {
- border: 1px solid rgba(255, 255, 255, 0.2);
- padding: 8px 0;
- margin-bottom: 16px;
- }
- #csv-area .header {
- text-align: center;
- font-size: 18px;
- }
- #csv-area button {
- margin: 8px;
- }
- `).appendTo("head");
-
- // Your code here...
- // .toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];
- })();