- // ==UserScript==
- // @name:zh-CN 补充包合成器增强
- // @name Boosterpack_Enhance
- // @namespace https://blog.chrxw.com
- // @version 1.4
- // @description 补充包制作工具
- // @description:zh-CN 补充包制作工具
- // @author Chr_
- // @match https://steamcommunity.com/tradingcards/boostercreator*
- // @match https://steamcommunity.com//tradingcards/boostercreator/*
- // @license AGPL-3.0
- // @icon https://blog.chrxw.com/favicon.ico
- // @grant GM_addStyle
- // @grant GM_getResourceText
- // @require https://cdnjs.cloudflare.com/ajax/libs/tabulator/6.3.0/js/tabulator.min.js
- // @resource css https://cdnjs.cloudflare.com/ajax/libs/tabulator/6.3.0/css/tabulator_midnight.min.css
- // ==/UserScript==
-
- (() => {
- 'use strict';
-
- const g_boosterData = {};
- const g_faveriteBooster = new Set();
- let g_craftMode = "2";
-
- // 初始化
- setTimeout(async () => {
- loadFavorite();
- await initBoosterData();
- initPanel();
- }, 200);
-
- function genDiv(cls) {
- const d = document.createElement("div");
- d.className = cls;
- return d;
- }
- function genInput(cls) {
- const i = document.createElement("input");
- i.className = cls;
- return i;
- }
- function genSpan(name) {
- const s = document.createElement("span");
- s.textContent = name;
- return s;
- }
- function genCheckbox(name, cls, key = null, checked = false) {
- const l = document.createElement("label");
- const i = document.createElement("input");
- const s = genSpan(name);
- i.textContent = name;
- i.title = name;
- i.type = "checkbox";
- i.className = "fac_checkbox";
- i.checked = localStorage.getItem(key) === "true";
- l.title = name;
- l.appendChild(i);
- l.appendChild(s);
- return [l, i];
- }
- function genImage(url, cls = "bh-image") {
- const i = document.createElement("img");
- i.src = url;
- i.className = cls;
- return i;
- }
- function genButton(name, foo, cls = "bh-button") {
- const b = document.createElement("button");
- b.textContent = name;
- b.title = name;
- b.className = cls;
- b.addEventListener("click", foo);
- return b;
- }
- function genOption(name, value) {
- const o = document.createElement("option");
- o.textContent = name;
- o.value = value;
- return o;
- }
- function genSelector() {
- const s = document.createElement("select");
- s.appendChild(genOption("可交易", "2"));
- s.appendChild(genOption("不可交易", "3"));
- return s;
- }
-
- function initPanel() {
- const area = document.querySelector("div.booster_creator_area");
-
- const filterContainer = genDiv("bh-filter");
- area.appendChild(filterContainer);
-
- const iptSearch = genInput("bh-search");
- iptSearch.placeholder = "搜索名称 / AppId";
- let t = 0;
- iptSearch.addEventListener("keydown", () => {
- clearTimeout(t);
- t = setTimeout(updateFilter, 500);
- });
- filterContainer.appendChild(iptSearch);
-
- const [lblOnlyFavorite, chkOnlyFavorite] = genCheckbox("仅显示已收藏", "bh-checkbox", "bh-onlyfavorite", false);
- chkOnlyFavorite.addEventListener("change", updateFilter);
- filterContainer.appendChild(lblOnlyFavorite);
- const [lblOnlyCraftable, chkOnlyCraftable] = genCheckbox("仅显示可合成", "bh-checkbox", "bh-onlycraftable", false);
- chkOnlyCraftable.addEventListener("change", updateFilter);
- filterContainer.appendChild(lblOnlyCraftable);
-
- const btnSearch = genButton("清除过滤条件", () => {
- iptSearch.value = "";
- chkOnlyFavorite.checked = false;
- chkOnlyCraftable.checked = false;
- updateFilter();
- }, "bh-button");
- filterContainer.appendChild(btnSearch);
- filterContainer.appendChild(genSpan(""));
-
- const tabledata = Object.values(g_boosterData);
-
- const divRight = genDiv("bh-right");
- filterContainer.appendChild(divRight);
-
- const selPackPrefer = genSelector();
- selPackPrefer.addEventListener("change", (_) => {
- g_craftMode = selPackPrefer.value;
- console.log(g_craftMode);
- })
- divRight.appendChild(selPackPrefer);
-
- const btnBatchCraft = genButton("批量合成收藏的包", async () => {
- const favoriteItems = tabledata.filter(x => x.favorite && x.available);
- if (favoriteItems.length === 0) {
- alert("无可合成项目");
- } else {
- for (let fav of favoriteItems) {
- await doCraftBooster2(fav.appid, fav.contailer);
- await asleep(200);
- }
- }
- }, "bh-button-right");
- divRight.appendChild(btnBatchCraft);
-
- const tableContainer = genDiv("bh-table");
- area.appendChild(tableContainer);
-
- const rowMenu = [
- {
- label: "收藏 / 取消收藏",
- action: doEditFavorite,
- }, {
- label: "合成补充包",
- action: doCraftBooster,
- },
- ];
-
- const table = new Tabulator(tableContainer, {
- height: 600,
- data: tabledata,
- layout: "fitDataStretch",
- rowHeight: 40,
- rowContextMenu: rowMenu,
- initialSort: [
- { column: "favorite", dir: "desc" },
- ],
- columns: [
- { title: "AppId", field: "appid" },
- { title: "图片", field: "appid", formatter: appImageFormatter, headerSort: false, width: 100, resizable: false },
- { title: "名称", field: "fullName", width: 300 },
- { title: "张数", field: "cardSet" },
- { title: "宝珠", field: "gemPrice" },
- {
- title: "收藏",
- field: "favorite",
- formatter: "tickCross",
- sorter: "boolean",
- cellClick: (e, cell) => doEditFavorite(e, cell.getRow()),
- },
- { title: "合成", field: "available", formatter: "tickCross", sorter: "boolean" },
- { title: "操作", field: "available", frozen: true, formatter: operatorFormatter, headerSort: false },
- ],
- });
-
- window.addEventListener("hashchange", () => {
- const appId = location.hash.replace("#", "");
- iptSearch.value = appId;
- updateFilter();
- });
-
- window.addEventListener("beforeunload", () => {
- localStorage.setItem("bh-onlyfavorite", chkOnlyFavorite.checked);
- localStorage.setItem("bh-onlycraftable", chkOnlyCraftable.checked);
- });
-
- updateFilter();
-
- function doEditFavorite(e, row) {
- const cell = row.getCell("favorite");
- const newValue = !cell.getValue();
- cell.setValue(newValue);
- const appId = row.getCell("appid").getValue();
- const strAppId = `${appId}`;
- if (newValue) {
- g_faveriteBooster.add(strAppId);
- } else {
- g_faveriteBooster.delete(strAppId);
- }
- saveFavorite();
- }
-
- function doCraftBooster(e, row) {
- const appid = row.getCell("appid").getValue();
- const available = row.getCell("available").getValue();
- const container = row.getCell("container").getValue();
- if (available) {
- doCraftBooster2(appid, container);
- }
- }
-
- function updateFilter() {
- const filters = [{ field: "keywords", type: "like", value: iptSearch.value.trim(), matchAll: true }];
- if (chkOnlyFavorite.checked) {
- filters.push({ field: "favorite", type: "=", value: true });
- }
- if (chkOnlyCraftable.checked) {
- filters.push({ field: "available", type: "=", value: true });
- }
- table.setFilter(filters);
- }
-
- function appImageFormatter(cell, formatterParams, onRendered) {
- const appid = cell.getValue();
- const src = `https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/${appid}/capsule_231x87.jpg`;
- const image = genImage(src, "be-row-image");
- return image;
- };
-
- function operatorFormatter(cell, formatterParams, onRendered) {
- const data = cell.getRow().getData();
- return data.contailer;
- };
- };
-
- function doCraftBooster2(appid, contailer) {
- let btn = contailer.querySelector("button");
- if (btn) {
- btn.disabled = true;
- }
- craftBoosterpack(appid)
- .then((success) => {
- if (success) {
- g_boosterData[appid].available = false;
- contailer.innerHTML = "";
- contailer.appendChild(genSpan("合成成功"));
- btn = null;
- } else {
- btn.textContent = "合成失败";
- }
- })
- .catch((err) => {
- console.error(err);
- btn.textContent = "合成失败";
- }).finally(() => {
- if (btn) {
- btn.disabled = false;
- }
- });
- }
-
- // 读取补充包列表
- async function initBoosterData() {
- const gemPrice2SetCount = {
- 1200: 5,
- 1000: 6,
- 857: 7,
- 750: 8,
- 667: 9,
- 600: 10,
- 545: 11,
- 500: 12,
- 462: 13,
- 429: 14,
- 400: 15
- };
-
- const currentData = parseBoosterData(document.body.innerHTML);
-
- const nameEnDict = {};
- if (g_strLanguage !== "english") {
- const html = await loadSecondLanguage("english");
- const secondData = parseBoosterData(html);
-
- for (const { appid, name } of secondData) {
- if (appid && name) {
- nameEnDict[appid] = name;
- }
- }
- }
-
- for (const item of currentData) {
- const { appid, name, unavailable, price, series, available_at_time } = item;
- const intPrice = parseInt(price);
- if (appid && name && intPrice === intPrice) {
- const nameEn = nameEnDict[appid] ?? "";
- let fullName;
- let keywords;
- if (name === nameEn) {
- fullName = name;
- keywords = `${appid} ${name}`.toLowerCase();
- } else {
- fullName = `${name} (${nameEn})`;
- keywords = `${appid} ${name} ${nameEn}`.toLowerCase();
- }
-
- const cardSet = gemPrice2SetCount[intPrice] ?? 0;
- const favorite = g_faveriteBooster.has(`${appid}`);
-
- //生成按钮
- const contailer = genDiv();
- if (!available_at_time) {
- const benCraft = genButton("合成补充包", (e) => doCraftBooster2(appid, contailer), "bh-button");
- contailer.appendChild(benCraft);
- } else {
- const time = genSpan(available_at_time);
- time.className = "bh-tips";
- contailer.appendChild(time);
- }
-
- g_boosterData[appid] = {
- appid,
- fullName,
- gemPrice: intPrice,
- keywords,
- series,
- cardSet,
- available_at_time,
- available: !unavailable,
- favorite,
- contailer,
- };
- }
- }
- }
-
- function parseBoosterData(html) {
- const matchJson = new RegExp(/CBoosterCreatorPage\.Init\(([\s\S]+}]),\s*parseFloat/);
- const result = html.match(matchJson);
- if (result) {
- const json = result[1];
- return JSON.parse(json);
- } else {
- return [];
- }
- }
-
- function loadFavorite() {
- const value = localStorage.getItem("be_faviorite") ?? "";
- const arr = value.split('|').filter(x => x);
-
- g_faveriteBooster.clear();
- for (const item of arr) {
- g_faveriteBooster.add(item);
- }
- }
-
- function saveFavorite() {
- const value = Array.from(g_faveriteBooster).join('|');
- console.log(g_faveriteBooster);
- localStorage.setItem("be_faviorite", value);
- }
-
- // 加载第二语言
- function loadSecondLanguage(lang) {
- return new Promise((resolve, reject) => {
- fetch(
- `https://steamcommunity.com/tradingcards/boostercreator/?l=${lang}`,
- {
- method: "GET",
- credentials: "include",
- })
- .then((response) => {
- return response.text();
- })
- .then((text) => {
- resolve(text);
- })
- .catch((err) => {
- reject(err);
- });
- });
- }
-
- // 合成补充包
- function craftBoosterpack(appId) {
- return new Promise((resolve, reject) => {
- const formData = new FormData();
- formData.append("sessionid", g_sessionID);
- formData.append("appid", appId);
- formData.append("series", "1");
- formData.append("tradability_preference", g_craftMode);
-
- fetch(
- "https://steamcommunity.com/tradingcards/ajaxcreatebooster/",
- {
- method: "POST",
- body: formData,
- credentials: "include",
- })
- .then(response => {
- return response.json();
- })
- .then((json) => {
- if (json.purchase_result) {
- const { success } = json.purchase_result;
- resolve(success === 1);
- }
- resolve(false);
- })
- .catch((err) => {
- reject(err);
- });
- });
- }
-
- //异步延时
- function asleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- })();
-
- GM_addStyle(GM_getResourceText("css"));
- GM_addStyle(`
- img.be-row-image {
- width: 90px;
- height: auto;
- }
- div.bh-filter {
- padding: 10px 0;
- }
- div.bh-filter > * {
- margin-right: 10px;
- }
- div.bh-right {
- display: inline;
- position: absolute;
- right: 10px;
- }
- div.bh-right > * {
- margin-left: 10px;
- }
- span.bh-tips {
- font-size: 10px;
- }
- `);