// ==UserScript==
// @name DownLoad Market History Record
// @namespace local.CR
// @version 1.1.0
// @description Generate XLSX File From Steam Market History Page
// @author CharRun
// @match https://steamcommunity.com/market/*
//// @require https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.15.1/shim.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.15.1/xlsx.full.min.js
// @grant GM_xmlhttpRequest
// @run-at document-end
// ==/UserScript==
"use strict";
/// <reference path="../node_modules/@types/tampermonkey" />
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
(function () {
var controlTabs = document.querySelector(".market_tab_well_tabs");
if (!g_steamID || !controlTabs) {
console.log("g_steamID:", g_steamID);
console.log("controlTabs:", controlTabs);
return;
}
var dialog;
var exportType = 0;
var exportLink = document.createElement("a");
var typeSelect = document.createElement("select");
exportLink.className = "market_tab_well_tab market_tab_well_tab_inactive";
exportLink.innerHTML = "<span class=\"market_tab_well_tab_contents\">\u5BFC\u51FA <select></select> \u8BB0\u5F55</span>";
typeSelect.innerHTML = "<option value=\"0\">\u5168\u90E8\u4EA4\u6613</option><option value=\"1\">\u6302\u5355\u6E05\u5355</option><option value=\"2\">\u5386\u53F2\u4EA4\u6613</option>";
typeSelect.onchange = function () { return (exportType = parseInt(typeSelect.value)); };
typeSelect.onclick = function (e) { return e.stopPropagation(); };
var parent = exportLink.querySelector("span");
parent.replaceChild(typeSelect, exportLink.querySelector("select"));
exportLink.onclick = function () { return export2XLSX(exportType); };
controlTabs.appendChild(exportLink);
function export2XLSX(type) {
return __awaiter(this, void 0, void 0, function () {
var current, total, currentTitle, listings, history, updateProgress, _a, sheetArray, wb;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (dialog) {
return [2 /*return*/];
}
current = 0;
total = 1;
currentTitle = "";
listings = [];
history = [];
dialog = CreateBlockingWaitDialog("\u6B63\u5728\u51C6\u5907EXCEL\u8868\u683C", "\u83B7\u53D6" + currentTitle + " \u4E2D " + 0.0 + "% Total:" + current + "/" + total);
updateProgress = function (progress) {
dialog.body("\u83B7\u53D6" + currentTitle + " \u4E2D " + (progress || "N/A") + "% Total:" + current + "/" + total);
};
_a = type;
switch (_a) {
case 0: return [3 /*break*/, 1];
case 1: return [3 /*break*/, 2];
case 2: return [3 /*break*/, 4];
}
return [3 /*break*/, 6];
case 1:
total = 2;
_b.label = 2;
case 2:
current++;
currentTitle = "挂单清单";
updateProgress();
return [4 /*yield*/, getAOAData(1, updateProgress)];
case 3:
listings = _b.sent();
if (type != 0) {
return [3 /*break*/, 6];
}
_b.label = 4;
case 4:
current++;
currentTitle = "历史交易";
updateProgress();
return [4 /*yield*/, getAOAData(2, updateProgress)];
case 5:
history = _b.sent();
return [3 /*break*/, 6];
case 6:
dialog && dialog.Dismiss();
dialog = null;
sheetArray = [];
if (!listings.length && !history.length) {
ShowConfirmDialog("下载数据表格", "获取记录出错,记录空", "确定");
return [2 /*return*/];
}
if (listings.length) {
sheetArray.push({
sheet: aoa2sheet(listings, "\u7528\u6237:" + g_steamID + "-\u4EA4\u6613\u5E02\u573A\u6302\u5355\u8BB0\u5F55-\u5BFC\u51FA\u65E5\u671F:" + new Date().toLocaleString()),
name: "挂单记录"
});
}
if (history.length) {
sheetArray.push({
sheet: aoa2sheet(history, "\u7528\u6237:" + g_steamID + "-\u4EA4\u6613\u5E02\u573A\u5386\u53F2\u8BB0\u5F55-\u5BFC\u51FA\u65E5\u671F:" + new Date().toLocaleString()),
name: "历史记录"
});
}
wb = sheet2WorkBookData(sheetArray);
ShowConfirmDialog("下载数据表格", "\u7528\u6237:" + g_steamID + "-\u4EA4\u6613\u5E02\u573A\u5386\u53F2\u8BB0\u5F55.xlsx", "下载", "取消").done(function () {
createDownloadStuffAndStartDownload(wb, "\u7528\u6237:" + g_steamID + "-\u4EA4\u6613\u5E02\u573A\u5386\u53F2\u8BB0\u5F55.xlsx");
});
return [2 /*return*/];
}
});
});
}
function getAOAData(type, update) {
return __awaiter(this, void 0, void 0, function () {
var aoa, start, total, success;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
aoa = [];
start = 0;
total = 1000;
success = true;
_a.label = 1;
case 1: return [4 /*yield*/, getMarketData(type, start).then(function (res) {
var jsonData = {};
try {
jsonData = JSON.parse(res.responseText);
}
catch (e) {
jsonData.success = false;
}
if (!jsonData.success) {
console.log("COLLECT DATA ERROR");
success = false;
dialog && dialog.Dismiss();
dialog = null;
ShowAlertDialog("交易市场历史记录", "获取历史交易数据失败", "确定");
return;
}
start += jsonData.pagesize;
total = jsonData.total_count;
aoa = aoa.concat(_handleTableData(jsonData, type));
var percent = Number((start / total) * 100).toFixed(2);
update && update(percent);
}, console.log)];
case 2:
_a.sent();
_a.label = 3;
case 3:
if (start < total && success) return [3 /*break*/, 1];
_a.label = 4;
case 4:
if (!success) {
aoa = [];
}
console.log(aoa);
return [2 /*return*/, aoa];
}
});
});
}
function getMarketData(type, start) {
return new Promise(function (res, rej) {
var url;
if (type == 1) {
url = "https://steamcommunity.com/market/mylistings?count=100&start=" + (start ? start : 0);
}
else {
url = "https://steamcommunity.com/market/myhistory/render/?query=&count=100&start=" + (start ? start : 0);
}
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: res,
onerror: rej
});
});
}
function _handleTableData(data, type) {
var recode = document.createElement("body");
var regexp = /CreateItemHoverFromContainer\([\s\S]*?,(?<eid>[\s\S]*?),(?<appid>[\s\S]*?),(?<contextid>[\s\S]*?),(?<classid>[\s\S]*?),(?<instanceid>[\s\S]*?)\);/gi;
recode.innerHTML = data.results_html;
var assets = data.assets;
var assetsChart = {};
var t;
while ((t = regexp.exec(data.hovers))) {
var eid = t[1].trim().replace(/^'|'$/gi, "");
var appid = t[2].trim().replace(/^'|'$/gi, "");
var classid = t[4].trim().replace(/^'|'$/gi, "");
var contextid = t[3].trim().replace(/^'|'$/gi, "");
var instanceid = t[5].trim().replace(/^'|'$/gi, "");
assetsChart[eid] = { appid: appid, classid: classid, contextid: contextid, instanceid: instanceid };
}
var getAsset = function (id, name) {
var e = assetsChart[id];
var asset;
try {
asset = assets[e.appid][e.contextid][e.classid];
}
catch (e) {
//stupid design no match
var deep_1 = function (obj, key, value) {
if (obj[key] && obj[key] == value) {
return obj;
}
else {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var _obj = obj[keys[i]];
if (Object.prototype.toString.call(_obj) === "[object Object]") {
var res = deep_1(_obj, key, value);
if (res[key] == value) {
return res;
}
}
}
return {};
}
};
asset = deep_1(assets, "name", name);
}
return asset;
};
var aoa = []; //array[]
//let array = Array(19).fill(null);
//array[0] itemName
//array[1] itemType
//array[2] tradeCurrency
//array[3] unitPrice
//array[4] tradeQTY
//array[5] totalPrice
//array[6] tradeType
//array[7] tradeUserName
//array[8] tradeUserLink
//array[9] listedDate
//array[10] actedDate
//array[11] itemAppId
//array[12] itemClassId
//array[13] itemContextId
//array[14] itemInstanceId
//array[15] itemMarketFeeApp
//array[16] itemMarketHashName
//array[17] itemUnownedContextId
//array[18] itemUnownedId
if (type === 1) {
var sellRows = recode.querySelectorAll(".my_listing_section:first-child .market_listing_row");
var orderRows = recode.querySelectorAll(".my_listing_section:last-child .market_listing_row");
for (var i = 0; i < sellRows.length; i++) {
var array = Array(19).fill("N/A");
var row = sellRows[i];
var rId = row.querySelector("span.market_listing_item_name") ||
row.querySelector("img.market_listing_item_img") || { id: "null" };
var id = rId.id;
var tradeType = "SellListings";
var name_1 = (row.querySelector(".market_listing_item_name")).innerText.trim();
var listedDate = (row.querySelector(".market_listing_listed_date")).innerText.trim();
var priceText = (row.querySelector(".market_listing_price span span:last-child")).innerText
.replace(/[\(\)]/gi, "")
.trim();
var priceString = priceText.replace(/[^0-9\.]/g, "") || "0";
var tradeCurrency = priceText.replace(priceString, "").trim() || null;
var unitPrice = parseFloat(priceString);
var asset = getAsset(id, name_1);
array[0] = asset.name || name_1 || null;
array[1] = asset.type || null;
array[2] = tradeCurrency || null;
array[3] = unitPrice || null;
array[4] = 1 || null;
array[5] = unitPrice || null;
array[6] = tradeType || null;
array[7] = null;
array[8] = null;
array[9] = listedDate || null;
array[10] = null;
array[11] = asset.appid || null;
array[12] = asset.classid || null;
array[13] = asset.contextid || null;
array[14] = asset.instanceid || null;
array[15] = asset.market_fee_app || null;
array[16] = asset.market_hash_name || null;
array[17] = asset.unowned_contextid || null;
array[18] = asset.unowned_id || null;
aoa.push(array);
}
for (var i = 0; i < orderRows.length; i++) {
var array = Array(19).fill("N/A");
var row = orderRows[i];
var rId = row.querySelector("span.market_listing_item_name") ||
row.querySelector("img.market_listing_item_img") || { id: "null" };
var id = rId.id;
var tradeType = "OrderListings";
var name_2 = (row.querySelector(".market_listing_item_name")).innerText.trim();
var priceText = ((row.querySelector(".market_listing_price ").lastChild).nodeValue).trim();
var priceString = priceText.replace(/[^0-9\.]/g, "") || "0";
var tradeCurrency = priceText.replace(priceString, "").trim() || null;
var unitPrice = parseFloat(priceString);
var tradeQTYString = (row.querySelectorAll(".market_listing_price")[1]).innerText.trim();
var tradeQTY = parseInt(tradeQTYString);
var totalPrice = unitPrice * tradeQTY;
var asset = getAsset(id, name_2);
array[0] = asset.name || name_2 || null;
array[1] = asset.type || null;
array[2] = tradeCurrency || null;
array[3] = unitPrice || null;
array[4] = tradeQTY || null;
array[5] = totalPrice || null;
array[6] = tradeType || null;
array[7] = null;
array[8] = null;
array[9] = null;
array[10] = null;
array[11] = asset.appid || null;
array[12] = asset.classid || null;
array[13] = asset.contextid || null;
array[14] = asset.instanceid || null;
array[15] = asset.market_fee_app || null;
array[16] = asset.market_hash_name || null;
array[17] = asset.unowned_contextid || null;
array[18] = asset.unowned_id || null;
aoa.push(array);
}
}
else if (type === 2) {
console.log(aoa);
var rows = recode.querySelectorAll(".market_listing_row.market_recent_listing_row");
for (var i = 0; i < rows.length; i++) {
var array = Array(19).fill("N/A");
var row = rows[i];
var rId = row.querySelector("span.market_listing_item_name") ||
row.querySelector("img.market_listing_item_img") || { id: "null" };
var id = rId.id;
var name_3 = (row.querySelector(".market_listing_item_name")).innerText.trim();
var priceText = (row.querySelector(".market_listing_price")).innerText.trim();
var priceString = priceText.replace(/[^0-9\.]/g, "") || "0";
var tradeCurrency = priceText.replace(priceString, "").trim() || null;
var unitPrice = parseFloat(priceString);
var tradeUser = row.querySelector("div.market_listing_right_cell.market_listing_whoactedwith > span > span > a");
var tradeUserLink = void 0;
var tradeUserName = void 0;
if (tradeUser) {
tradeUserLink = tradeUser.href;
tradeUserName = tradeUser.children[0].title;
}
var type_1 = (row.querySelector(".market_listing_gainorloss")).innerText.trim();
var tradeType = type_1 == "+" ? "Buy" : type_1 == "-" ? "Sell" : "Revoke";
var listedDate = (row.querySelector(".market_listing_listed_date")).innerText.trim();
var actedDate = (row.querySelectorAll(".market_listing_listed_date")[1]).innerText.trim();
var asset = getAsset(id, name_3);
array[0] = asset.name || name_3 || null;
array[1] = asset.type || null;
array[2] = tradeCurrency || null;
array[3] = unitPrice || null;
array[4] = 1 || null;
array[5] = unitPrice || null;
array[6] = tradeType || null;
array[7] = tradeUserName || null;
array[8] = tradeUserLink || null;
array[9] = listedDate || null;
array[10] = actedDate || null;
array[11] = asset.appid || null;
array[12] = asset.classid || null;
array[13] = asset.contextid || null;
array[14] = asset.instanceid || null;
array[15] = asset.market_fee_app || null;
array[16] = asset.market_hash_name || null;
array[17] = asset.unowned_contextid || null;
array[18] = asset.unowned_id || null;
aoa.push(array);
}
}
return aoa;
}
function sheet2WorkBookData(sheets) {
var workbook = {
SheetNames: [],
Sheets: {}
};
console.log(sheets);
sheets.forEach(function (val, index) {
var name = val.name || "sheet" + index;
name = workbook.SheetNames.indexOf(name) > -1 ? name + "(1)" : name;
workbook.SheetNames.push(name);
workbook.Sheets[name] = val.sheet;
});
var wOpts = {
bookType: "xlsx",
bookSST: false,
type: "binary"
};
return XLSX.write(workbook, wOpts);
}
function createDownloadStuffAndStartDownload(workBookData, filename) {
var stringToArrayBuffer = function (str) {
var buffer = new ArrayBuffer(str.length);
var v = new Uint8Array(buffer);
for (var i = 0; i != str.length; ++i)
v[i] = str.charCodeAt(i) & 0xff;
return buffer;
};
var blob = new Blob([stringToArrayBuffer(workBookData)], {
type: "application/octet-stream"
});
var url = URL.createObjectURL(blob);
var downloadLink = document.createElement("a");
downloadLink.href = url;
downloadLink.download = filename || "Steam";
var event;
if (window.MouseEvent) {
event = new MouseEvent("click");
}
else {
event = document.createEvent("MouseEvents");
event.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
}
downloadLink.dispatchEvent(event);
}
function CreateBlockingWaitDialog(title, body) {
if (title === void 0) { title = ""; }
if (body === void 0) { body = ""; }
var _this = ShowBlockingWaitDialog(title, body);
_this.title = function (s) {
return (_this.m_$Content[0].querySelector(".newmodal_header_border .title_text").innerText = s);
};
_this.body = function (s) {
return (_this.m_$Content[0].querySelector(".newmodal_content .waiting_dialog_throbber").nextSibling.textContent = s);
};
return _this;
}
function aoa2sheet(aoa, t) {
var offset = 0;
var title = [t || "Steam Market Recode Export"];
var Subtitle = [
"This tool is not affiliated with Valve or Steam!. Powered by JS-XLSX. Create by CharRun"
];
var Warning = ["导出数据仅供参考,以Steam Market 官方数据为准"];
var blank = [];
var header = [title, Subtitle, Warning, blank];
offset += 4;
var construct = {};
var _offset = 0;
for (var i = 0; i < aoa.length; i++) {
var type = aoa[i][6];
var currency = aoa[i][2];
if (!currency || type == "Revoke")
continue;
if (construct[type]) {
if (construct[type].indexOf(currency) < 0) {
construct[type].push(currency);
}
}
else {
construct[type] = [currency];
}
_offset = construct[type].length > _offset ? construct[type].length : _offset;
}
var types = Object.keys(construct);
var typeRow = ["Trade Type", null];
var currenciesRow = [null, null];
types.forEach(function (val) {
typeRow.push(val, null, null);
currenciesRow.push(null, "currency", "Total Price");
});
header.push(typeRow, currenciesRow);
offset += 2;
var startRow = offset + 1;
offset += _offset;
header.push([], []);
header.push([
"itemName",
"itemType",
"tradeCurrency",
"unitPrice",
"tradeQTY",
"totalPrice",
"tradeType",
"tradeUserName",
"tradeUserLink",
"listedDate",
"actedDate",
"itemAppId",
"itemClassId",
"itemInstanceId",
"itemMarketFeeApp",
"itemMarketHashName",
"itemUnownedContextId",
"itemUnownedId"
]);
offset += 3;
header = header.concat(aoa);
var sheet = XLSX.utils.aoa_to_sheet(header);
sheet["!merges"] = [{ s: { r: 0, c: 0 }, e: { r: 0, c: 17 } }];
sheet["!merges"] = [{ s: { r: 1, c: 0 }, e: { r: 1, c: 17 } }];
sheet["!merges"] = [{ s: { r: 2, c: 0 }, e: { r: 2, c: 17 } }];
var _loop_1 = function (i) {
var rB = 0;
types.forEach(function (val) {
rB += 3;
var curr = construct[val][i];
if (!curr) {
return;
}
var curP = String.fromCharCode(65 + rB) + startRow;
var tolP = String.fromCharCode(66 + rB) + startRow;
sheet[curP] = { t: "s", v: curr };
sheet[tolP] = {
t: "n",
f: "SUMIFS(F" + offset + ":F" + (offset + aoa.length - 1) + ",C" + offset + ":C" + (offset +
aoa.length -
1) + "," + curP + ",G" + offset + ":G" + (offset + aoa.length - 1) + ",\"" + val + "\")"
};
});
};
for (var i = 0; i < _offset; i++) {
_loop_1(i);
}
return sheet;
}
})();