Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name:zh-CN Steam 库存物品堆叠工具
- // @name Inventory_Stack_Helper_ru
- // @namespace https://blog.chrxw.com
- // @supportURL https://blog.chrxw.com/scripts.html
- // @contributionURL https://afdian.net/@chr233
- // @version 1.7
- // @description Steam Inventory Stack Helper
- // @description:zh-CN Steam 物品堆叠工具
- // @author Chr_
- // @match https://steamcommunity.com/profiles/*/inventory*
- // @match https://steamcommunity.com/id/*/inventory*
- // @license AGPL-3.0
- // @icon https://blog.chrxw.com/favicon.ico
- // @grant GM_addStyle
- // @downloadURL https://update.greasyfork.org/scripts/498326/Inventory_Stack_Helper.user.js
- // @updateURL https://update.greasyfork.org/scripts/498326/Inventory_Stack_Helper.meta.js
- // ==/UserScript==
- // Инициация
- (() => {
- "use strict";
- let token = document.querySelector("#application_config")?.getAttribute("data-loyalty_webapi_token");
- if (token) {
- token = token.replace(/"/g, "");
- }
- else {
- ShowPromptDialog("Не удалось прочитать токен, возможно, вам потребуется снова войти в систему");
- return;
- }
- const GObjs = addPanel();
- loadSetting();
- doFitInventory();
- //==================================================================================================
- function genBtn(text, title, onclick) {
- let btn = document.createElement("button");
- btn.textContent = text;
- btn.title = title;
- btn.className = "ish_button";
- btn.addEventListener("click", onclick);
- return btn;
- }
- function genSpan(text) {
- let span = document.createElement("span");
- span.textContent = text;
- return span;
- }
- function genNumber(value, placeholder, title, readonly = false, disabled = false) {
- const t = document.createElement("input");
- t.className = "ish_inputbox";
- t.placeholder = placeholder;
- t.title = title;
- t.type = "number";
- t.value = value;
- t.readOnly = readonly;
- t.disabled = disabled;
- return t;
- }
- function addPanel() {
- const btnArea = document.querySelector("div.inventory_links");
- const container = document.createElement("div");
- container.className = "ish_container"
- btnArea.insertAdjacentElement("afterend", container);
- const iptAppId = genNumber("", "2923300", "AppId",true,true);
- const iptContextId = genNumber("", "2", "ContextId",true,true);
- const iptDelay = genNumber("500", "Delay", "Интервал между запросами");
- const iptAmount = genNumber("500", "How many?", "Количество загружемых предметов");
- const btnStack = genBtn("Stuck", "Объединить предметы", doStack);
- const btnUnstack = genBtn("Unstack", "Разъединить предметы", doUnstack);
- const btnHelp = genBtn("❓", "Помощь", doHelp);
- const spStatus = genSpan("");
- container.appendChild(genSpan("AppId"));
- container.appendChild(iptAppId);
- container.appendChild(iptContextId);
- container.appendChild(genSpan("Delay"));
- container.appendChild(iptDelay);
- container.appendChild(genSpan("Max"));
- container.appendChild(iptAmount);
- container.appendChild(btnStack);
- container.appendChild(btnUnstack);
- container.appendChild(btnHelp);
- container.appendChild(spStatus);
- document.querySelectorAll('div.games_list_tabs>a').forEach(tab => {
- tab.addEventListener("click", doFitInventory);
- });
- document.querySelector("#responsive_inventory_select")?.addEventListener("change", doFitInventory);
- return { iptAppId, iptContextId, iptDelay, iptAmount, btnStack, btnUnstack, btnHelp, spStatus };
- }
- function doHelp() {
- const { script: { version } } = GM_info;
- console.log(GM_info);
- ShowAlertDialog("FAQ",
- [
- "<p>【AppId】: Выберите вкладку инвентаря, AppID и ContextID будут загружены автоматически</p>",
- "<p>【Delay】: Интервал между отправкой каждого сетевого запроса рекомендуется устанавливать более чем 100</p>",
- "<p>【Max】: Максимальное количества объединяемых предметов (по умолчанию 500). Если предметов много, скорректируете по своему усмотрению</p>",
- "<p>【Stuck】: Объедините одинаковые предметы в один </p>",
- "<p>【Unstuck】: Разъединить предметы </p>",
- `<p>【<a href="https://keylol.com/t954659-1-1" target="_blank">Блог</a>】 【<a href="https://blog.chrxw.com/scripts.html" target="_blank">Обратная связь</a>】</p>`,
- `<p>【Разработчик <a href="https://steamcommunity.com/id/Chr_" target="_blank">Chr_</a>】 【Версия ${version}】</p>`,
- ].join("<br>")
- )
- }
- function doFitInventory() {
- const { iptAppId, iptContextId } = GObjs;
- const { appid, contextid } = g_ActiveInventory;
- iptAppId.value = appid ?? "0";
- if (appid == 753) {
- iptContextId.value = "6";
- } else {
- iptContextId.value = contextid ?? "2";
- }
- }
- function doStack() {
- const { iptAppId, iptContextId, iptDelay, iptAmount, btnStack, btnUnstack, btnHelp, spStatus } = GObjs;
- const appId = parseInt(iptAppId.value);
- const contextId = parseInt(iptContextId.value);
- if (appId !== appId || contextId !== contextId) {
- ShowAlertDialog("Подсказка", "Пожалуйста, проверьте, правильно ли заполнены AppID и ContextID");
- return;
- }
- const delay = parseInt(iptDelay.value);
- if (delay !== delay) {
- ShowAlertDialog("Подсказка", "Пожалуйста, проверьте, правильно ли указана задержка");
- return;
- }
- const amount = parseInt(iptAmount.value);
- if (amount !== amount) {
- ShowAlertDialog("Подсказка", "Пожалуйста, проверьте, правильно ли указано максимальное количество");
- return;
- }
- saveSetting();
- spStatus.textContent = "Объединение [Загрузка инвентаря]";
- btnStack.style.display = "none";
- btnUnstack.style.display = "none";
- btnHelp.style.display = "none";
- loadInventory(appId, contextId, amount)
- .then(async (inv) => {
- if (!inv) {
- ShowAlertDialog("Подсказка", "Не удалось прочитать инвентарь, пожалуйста, проверьте, правильно ли заполнены AppID и ContextID");
- return;
- }
- const { assets } = inv;
- if (assets) {
- const itemGroup = {};
- for (let item of assets) {
- const { classid } = item;
- // Работает только со самоцветами и мешками самоцветов.
- if (appId === 753 && (classid != "667924416" && classid != "667933237")) {
- continue;
- }
- if (!itemGroup[classid]) {
- itemGroup[classid] = [];
- }
- itemGroup[classid].push(item);
- }
- let totalReq = 0;
- const todoList = [];
- for (let classId in itemGroup) {
- const items = itemGroup[classId];
- if (items.length > 1) {
- todoList.push(items);
- totalReq += items.length - 1;
- }
- }
- if (totalReq > 0) {
- const totalType = todoList.length;
- spStatus.textContent = `Объединение [Тип 0/${totalType} Запрос 0/${totalReq} 0.00%]`;
- let type = 1;
- let req = 1;
- for (let items of todoList) {
- for (let i = 1; i < items.length; i++) {
- await stackItem(iptAppId.value, items[i].assetid, items[0].assetid, items[i].amount);
- await asyncDelay(delay);
- const percent = (100 * req / totalReq).toFixed(2);
- spStatus.textContent = `Объединение [Тип ${type}/${totalType} Запрос ${req++}/${totalReq} ${percent}%]`;
- }
- type++;
- }
- }
- ShowAlertDialog("Подсказка", totalReq > 0 ? "Объединение завершено": "Больше нет предметов, которые можно объединить");
- } else {
- ShowAlertDialog("Подсказка", "Не удалось прочитать инвентарь, пожалуйста, проверьте, правильно ли заполнены AppID и ContextID");
- }
- })
- .catch((err) => {
- ShowAlertDialog("Подсказка", "Ошибка считывания инвентаря\r\n" + err);
- console.error(err);
- })
- .finally(() => {
- spStatus.textContent = "";
- btnStack.style.display = null;
- btnUnstack.style.display = null;
- btnHelp.style.display = null;
- g_ActiveInventory.m_owner.ReloadInventory(appId, contextId);
- });
- }
- function doUnstack() {
- const { iptAppId, iptContextId, iptDelay, iptAmount, btnStack, btnUnstack, btnHelp, spStatus } = GObjs;
- const appId = parseInt(iptAppId.value);
- const contextId = parseInt(iptContextId.value);
- if (appId !== appId || contextId !== contextId) {
- ShowAlertDialog("Подсказка", "Пожалуйста, проверьте, правильно ли заполнены AppID и ContextID");
- return;
- }
- const delay = parseInt(iptDelay.value);
- if (delay !== delay) {
- ShowAlertDialog("Подсказка", "Пожалуйста, проверьте, правильно ли указана задержка");
- return;
- }
- const amount = parseInt(iptAmount.value);
- if (amount !== amount) {
- ShowAlertDialog("Подсказка", "Пожалуйста, проверьте, правильно ли указано максимальное количество");
- return;
- }
- saveSetting();
- spStatus.textContent = "Разборка [Загрузка инвентаря]";
- btnStack.style.display = "none";
- btnUnstack.style.display = "none";
- btnHelp.style.display = "none";
- loadInventory(appId, contextId, amount)
- .then(async (inv) => {
- if (!inv) {
- ShowAlertDialog("Подсказка", "Не удалось прочитать инвентарь, пожалуйста, проверьте, правильно ли заполнены AppID и ContextID");
- return;
- }
- const { assets } = inv;
- if (assets) {
- const itemGroup = [];
- let totalReq = 0;
- for (let item of assets) {
- const { classid, amount } = item;
- // Работает только со самоцветами и мешками самоцветов.
- if (appId === 753 && (classid != "667924416" && classid != "667933237")) {
- continue;
- }
- const num = parseInt(amount);
- if (num > 1) {
- item.amount = num;
- itemGroup.push(item);
- totalReq += num - 1;
- }
- }
- if (totalReq > 0) {
- const totalType = itemGroup.length;
- spStatus.textContent = `Разъединение [Тип 0/${totalType} Запрос 0/${totalReq} 0.00%]`;
- let type = 1;
- let req = 1;
- for (let item of itemGroup) {
- for (let i = 1; i < item.amount; i++) {
- await unStackItem(iptAppId.value, item.assetid, 1);
- await asyncDelay(delay);
- const percent = (100 * req / totalReq).toFixed(2);
- spStatus.textContent = `Разъединение [Тип ${type}/${totalType} Запрос ${req++}/${totalReq} ${percent}%]`;
- }
- type++;
- }
- }
- ShowAlertDialog("Подсказка", totalReq > 0 ? "Разъединение завершено" : "Больше нет предметов, которые можно разъединить");
- } else {
- ShowAlertDialog("Подсказка", "Не удалось прочитать инвентарь, пожалуйста, проверьте, правильно ли заполнены AppID и ContextID");
- }
- })
- .catch((err) => {
- ShowAlertDialog("Подсказка", "Ошибка считывания инвентаря\r\n" + err);
- console.error(err);
- })
- .finally(() => {
- spStatus.textContent = "";
- btnStack.style.display = null;
- btnUnstack.style.display = null;
- btnHelp.style.display = null;
- g_ActiveInventory.m_owner.ReloadInventory(appId, contextId);
- });
- }
- function loadSetting() {
- const { iptAppId, iptContextId, iptDelay, iptAmount } = GObjs;
- // iptAppId.value = localStorage.getItem("ish_appId") ?? "";
- // iptContextId.value = localStorage.getItem("ish_contextId") ?? "";
- iptDelay.value = localStorage.getItem("ish_delay") ?? "500";
- iptAmount.value = localStorage.getItem("ish_amount") ?? "500";
- }
- function saveSetting() {
- const { iptAppId, iptContextId, iptDelay, iptAmount } = GObjs;
- // localStorage.setItem("ish_appId", iptAppId.value);
- // localStorage.setItem("ish_contextId", iptContextId.value);
- localStorage.setItem("ish_delay", iptDelay.value);
- localStorage.setItem("ish_amount", iptAmount.value);
- }
- //==================================================================================================
- // Delay
- function asyncDelay(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- // Чтение инвентаря
- function loadInventory(appId, contextId, count) {
- return new Promise((resolve, reject) => {
- fetch(`https://steamcommunity.com/inventory/${g_steamID}/${appId}/${contextId}?l=${g_strLanguage}&count=${count}`)
- .then(async (response) => {
- response.json().then(json => {
- resolve(json);
- })
- })
- .catch((err) => {
- fetch(`https://steamcommunity.com/inventory/${g_steamID}/${appId}/${contextId}?l=${g_strLanguage}&count=${count}`)
- .then(async (response) => {
- response.json().then(json => {
- resolve(json);
- })
- })
- .catch((err) => {
- fetch(`https://steamcommunity.com/inventory/${g_steamID}/${appId}/${contextId}?l=${g_strLanguage}&count=${count}`)
- .then(async (response) => {
- response.json().then(json => {
- resolve(json);
- })
- })
- .catch((err) => {
- console.error(err);
- reject(`Не удалось прочитать инвентарь ${err}`);
- });
- });
- });
- })
- }
- // Объединение предметов
- function stackItem(appId, fromAssetId, destAssetId, quantity) {
- return new Promise((resolve, reject) => {
- fetch(
- `https://api.steampowered.com/IInventoryService/CombineItemStacks/v1/`,
- {
- method: "POST",
- body: `access_token=${token}&appid=${appId}&fromitemid=${fromAssetId}&destitemid=${destAssetId}&quantity=${quantity}&steamid=${g_steamID}`,
- headers: {
- "content-type":
- "application/x-www-form-urlencoded; charset=UTF-8",
- },
- }
- )
- .then((response) => {
- response.json().then(json => {
- const { success } = json;
- resolve(success);
- })
- })
- .catch((err) => {
- console.error(err);
- reject(`Не удалось объединить предметы ${err}`);
- });
- });
- }
- // Разъединение
- function unStackItem(appId, itemAssetId, quantity) {
- return new Promise((resolve, reject) => {
- fetch(
- `https://api.steampowered.com/IInventoryService/SplitItemStack/v1/`,
- {
- method: "POST",
- body: `access_token=${token}&appid=${appId}&itemid=${itemAssetId}&quantity=${quantity}&steamid=${g_steamID}`,
- headers: {
- "content-type":
- "application/x-www-form-urlencoded; charset=UTF-8",
- },
- }
- )
- .then((response) => {
- response.json().then(json => {
- const { success } = json;
- resolve(success);
- })
- })
- .catch((err) => {
- console.error(err);
- reject(`Не удалось разъединить предметы ${err}`);
- });
- });
- }
- })();
- GM_addStyle(`
- div.ish_container {
- display: inline;
- }
- div.ish_container > * {
- margin: 5px 5px 10px 5px;
- }
- input.ish_inputbox {
- width: 70px;
- padding: 5px;
- }
- input::-webkit-outer-spin-button,
- input::-webkit-inner-spin-button {
- -webkit-appearance: none;
- margin: 0;
- }
- input.ish_inputbox:nth-of-type(3),
- input.ish_inputbox:nth-of-type(4){
- width: 50px;
- }
- `);
Advertisement
Add Comment
Please, Sign In to add comment