- // ==UserScript==
- // @name:zh-CN Steam快速添加购物车
- // @name Fast_Add_Cart
- // @namespace https://blog.chrxw.com
- // @supportURL https://blog.chrxw.com/scripts.html
- // @contributionURL https://afdian.com/@chr233
- // @version 4.6
- // @description:zh-CN 超级方便的添加购物车体验, 不用跳转商店页, 附带导入导出购物车功能.
- // @description Add to cart without redirect to cart page, also provide import/export cart feature.
- // @author Chr_
- // @match https://store.steampowered.com/*
- // @license AGPL-3.0
- // @icon https://blog.chrxw.com/favicon.ico
- // @grant GM_addStyle
- // @grant GM_setClipboard
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_registerMenuCommand
- // ==/UserScript==
-
- (async () => {
- "use strict";
-
- // 多语言
- const LANG = {
- ZH: {
- langName: "中文",
- changeLang: "修改插件语言",
- facInputBoxPlaceHolder:
- "一行一条, 自动忽略【#】后面的内容, 支持的格式如下: (自动保存)",
- storeLink: "商店链接",
- steamDBLink: "DB链接",
- import: "导入",
- importDesc: "从文本框批量添加购物车(从上到下导入)",
- importDesc2: "当前页面无法导入购物车",
- export: "导出",
- exportDesc: "将购物车内容导出至文本框",
- exportConfirm: "输入框中含有内容, 请选择操作?",
- exportConfirmReplace: "覆盖原有内容",
- exportConfirmAppend: "添加到最后",
- copy: "复制",
- copyDesc: "复制文本框中的内容",
- copyDone: "复制到剪贴板成功",
- reset: "清除",
- resetDesc: "清除文本框和已保存的数据",
- resetConfirm: "您确定要清除文本框和已保存的数据吗?",
- history: "历史",
- historyDesc: "查看购物车历史记录",
- reload: "刷新",
- reloadDesc: "重新读取保存的购物车内容",
- reloadConfirm: "您确定要重新读取保存的购物车数据吗?",
- goBack: "返回",
- goBackDesc: "返回你当前的购物车",
- clear: "清空购物车",
- clearDesc: "清空购物车",
- clearConfirm: "您确定要移除所有您购物车中的物品吗?",
- help: "帮助",
- helpDesc: "显示帮助",
- helpTitle: "插件版本",
- formatError: "格式有误",
- chooseSub: "请选择SUB",
- operation: "操作中……",
- operationDone: "操作完成",
- addCart: "添加购物车",
- addCartTips: "添加到购物车……",
- addCartErrorSubNotFount: "未识别到SubID",
- noSubDesc: "可能尚未发行或者是免费游戏",
- inCart: "在购物车中",
- importingTitle: "正在导入购物车……",
- add: "添加",
- toCart: "到购物车",
- tips: "提示",
- ok: "是",
- no: "否",
- fetchingSubs: "读取可用SUB",
- noSubFound: "未找到可用SUB",
- networkError: "网络错误",
- addCartSuccess: "添加购物车成功",
- addCartError: "添加购物车失败",
- networkRequestError: "网络请求失败",
- unknownError: "未知错误",
- unrecognizedResult: "返回了未知结果",
- batchExtract: "批量提取",
- batchExtractDone: "批量提取完成",
- batchDesc: "AppID已提取, 可以在购物车页批量导入",
- onlyOnsale: " 仅打折",
- onlyOnsaleDesc: "勾选后批量导入时仅导入正在打折的游戏.",
- onlyOnsaleDesc2: "勾选后批量导出时仅导出正在打折的游戏.",
- notOnSale: "尚未打折, 跳过",
- },
- EN: {
- langName: "English",
- changeLang: "Change plugin language",
- facInputBoxPlaceHolder:
- "One line one item, ignore the content after #, support format: (auto save)",
- storeLink: "Store link",
- steamDBLink: "DB link",
- import: "Import",
- importDesc: "Batch add cart from textbox (from top to bottom)",
- importDesc2: "Current page can't import cart",
- export: "Export",
- exportDesc: "Export cart content to textbox",
- exportConfirm: "Textbox contains content, please choose operation?",
- exportConfirmReplace: "Replace original content",
- exportConfirmAppend: "Append to the end",
- copy: "Copy",
- copyDesc: "Copy textbox content",
- copyDone: "Copy to clipboard success",
- reset: "Reset",
- resetDesc: "Clear textbox and saved data",
- resetConfirm: "Are you sure to clear textbox and saved cart data?",
- history: "History",
- historyDesc: "View cart history",
- reload: "Reload",
- reloadDesc: "Reload saved cart date",
- reloadConfirm: "Are you sure to reload saved cart data?",
- goBack: "Back",
- goBackDesc: "Back to your cart",
- clear: "Clear",
- clearDesc: "Clear cart",
- clearConfirm: "Are you sure to remove all items in your cart?",
- help: "Help",
- helpDesc: "Show help",
- helpTitle: "Plugin Version",
- formatError: "Format error",
- chooseSub: "Please choose SUB",
- operation: "Operation in progress……",
- operationDone: "Operation done",
- addCart: "Add cart",
- addCartTips: "Adding to cart……",
- addCartErrorSubNotFount: "Unrecognized SubID",
- noSubDesc: "Maybe not released or free game",
- inCart: "In cart",
- importingTitle: "Importing cart……",
- add: "Add",
- toCart: "To cart",
- tips: "Tips",
- ok: "OK",
- no: "No",
- fetchingSubs: "Fetching available SUB",
- noSubFound: "No available SUB",
- networkError: "Network error",
- addCartSuccess: "Add cart success",
- addCartError: "Add cart failed",
- networkRequestError: "Network request failed",
- unknownError: "Unknown error",
- unrecognizedResult: "Returned unrecognized result",
- batchExtract: "Extract Items",
- batchExtractDone: "Batch Extract Done",
- batchDesc: "AppID list now saved, goto cart page to use batch import.",
- onlyOnsale: " Only on sale",
- onlyOnsaleDesc:
- "If checked, script will ignore games that is not on sale when import cart.",
- onlyOnsaleDesc2:
- "If checked, script will ignore games that is not on sale when export cart.",
- notOnSale: "Not on sale, skip",
- },
- };
-
- // 判断语言
- let language = GM_getValue("lang", "ZH");
- if (!language in LANG) {
- language = "ZH";
- GM_setValue("lang", language);
- }
- // 获取翻译文本
- const t = (key) => LANG[language][key] || key;
-
- {
- // 自动弹出提示
- const languageTips = GM_getValue("languageTips", true);
- if (languageTips && language === "ZH") {
- if (!document.querySelector("html").lang.startsWith("zh")) {
- ShowConfirmDialog(
- "tips",
- "Fast add cart now support English, switch?",
- "Using English",
- "Don't show again"
- )
- .done(() => {
- GM_setValue("lang", "EN");
- GM_setValue("languageTips", false);
- window.location.reload();
- })
- .fail((bool) => {
- if (bool) {
- showAlert(
- "",
- "You can switch the plugin's language using TamperMonkey's menu."
- );
- GM_setValue("languageTips", false);
- }
- });
- }
- }
- //注册(不可用)菜单
- GM_registerMenuCommand(`${t("changeLang")} (${t("langName")})`, () => {
- switch (language) {
- case "EN":
- language = "ZH";
- break;
- case "ZH":
- language = "EN";
- break;
- }
- GM_setValue("lang", language);
- window.location.reload();
- });
- }
-
- //获取商店语言和区域
- const { LANGUAGE: storeLanguage, COUNTRY: userCountry } = JSON.parse(document.querySelector("#application_config")?.getAttribute("data-config") ?? "{}");
- const { webapi_token: accessToken } = JSON.parse(document.querySelector("#application_config")?.getAttribute("data-store_user_config") ?? "{}");
-
- const G_Objs = {};
-
- //初始化
- const pathname = window.location.pathname;
- if (
- pathname === "/search/" ||
- pathname === "/" ||
- pathname.startsWith("/tags/")
- ) {
- //搜索页,主页,标签页
- return;
-
- } else if (
- pathname.startsWith("/publisher/") ||
- pathname.startsWith("/franchise/") ||
- pathname.startsWith("/developer/")
- ) {
- //发行商主页
- return;
-
- } else if (
- pathname.startsWith("/app/") ||
- pathname.startsWith("/sub/") ||
- pathname.startsWith("/bundle/")
- ) {
- //商店详情页
- return;
-
- } else if (pathname.startsWith("/wishlist/")) {
- //愿望单页
- return;
-
- } else if (pathname.startsWith("/cart")) {
- //购物车页
-
- function genBr() {
- return document.createElement("br");
- }
- function genBtn(text, title, onclick) {
- let btn = document.createElement("button");
- btn.textContent = text;
- btn.title = title;
- btn.className = "btn_medium btnv6_blue_hoverfade fac_cartbtns";
- btn.addEventListener("click", onclick);
- return btn;
- }
- function genSpan(text) {
- let span = document.createElement("span");
- span.textContent = text;
- return span;
- }
- function genTxt(value, placeholder) {
- const t = document.createElement("textarea");
- t.className = "fac_inputbox";
- t.placeholder = placeholder;
- t.value = value;
- return t;
- }
- function genChk(name, title, checked = false) {
- const l = document.createElement("label");
- const i = document.createElement("input");
- const s = genSpan(name);
- i.textContent = name;
- i.title = title;
- i.type = "checkbox";
- i.className = "fac_checkbox";
- i.checked = checked;
- l.title = title;
- l.appendChild(i);
- l.appendChild(s);
- return [l, i];
- }
-
- const savedCart = GM_getValue("fac_cart") ?? "";
- const placeHolder = [
- t("facInputBoxPlaceHolder"),
- `1. ${t("storeLink")}: https://store.steampowered.com/app/xxx`,
- `2. ${t("steamDBLink")}: https://steamdb.info/app/xxx`,
- "3. appID: xxx a/xxx app/xxx",
- "4. subID: s/xxx sub/xxx",
- "5. bundleID: b/xxx bundle/xxx",
- ].join("\n");
-
- const inputBox = genTxt(savedCart, placeHolder);
-
- function fitInputBox() {
- inputBox.style.height =
- Math.min(inputBox.value.split("\n").length * 20 + 20, 900).toString() +
- "px";
- }
-
- inputBox.addEventListener("input", fitInputBox);
- G_Objs.inputBox = inputBox;
- fitInputBox();
-
- const originResetBtn = document.querySelector("div.remove_ctn");
- if (originResetBtn != null) {
- originResetBtn.style.display = "none";
- }
-
- const [lblDiscount, chkDiscount] = genChk(
- t("onlyOnsale"),
- t("onlyOnsaleDesc"),
- GM_getValue("fac_discount") ?? false
- );
- G_Objs.chkDiscount = chkDiscount;
-
- const btnImport = genBtn(`🔼${t("import")}`, t("importDesc"), async () => {
- inputBox.value = await importCart(
- inputBox.value,
- chkDiscount.checked
- );
- });
-
- const histryPage = pathname.search("history") !== -1;
- if (histryPage) {
- btnImport.disabled = true;
- btnImport.title = t("importDesc2");
- }
-
- const [lblDiscount2, chkDiscount2] = genChk(
- t("onlyOnsale"),
- t("onlyOnsaleDesc2"),
- GM_getValue("fac_discount2") ?? false
- );
- G_Objs.chkDiscount2 = chkDiscount2;
-
- const btnExport = genBtn(`🔽${t("export")}`, t("exportDesc"), async () => {
- let currentValue = inputBox.value.trim();
- const now = new Date().toLocaleString();
-
- var data = await exportCart(chkDiscount2.checked);
-
- if (currentValue !== "") {
- ShowConfirmDialog(
- "",
- t("exportConfirm"),
- t("exportConfirmReplace"),
- t("exportConfirmAppend")
- )
- .done(() => {
- inputBox.value = `========【${now}】=========\n` + data;
- fitInputBox();
- })
- .fail((bool) => {
- if (bool) {
- inputBox.value = currentValue + `\n========【${now}】=========\n` + data;
- fitInputBox();
- }
- });
- } else {
- inputBox.value =
- `========【${now}】=========\n` + exportCart(chkDiscount2.checked);
- fitInputBox();
- }
- });
-
- const btnConvertToGift = genBtn("转送礼", "将购物车项目转换为送礼", () => {
- editCart(true, false);
- });
-
- const btnConvertToSelf = genBtn("转自用", "将购物车项目转换为为自己购买", () => {
- editCart(false, false);
- });
-
- const btnCopy = genBtn(`📋${t("copy")}`, t("copyDesc"), () => {
- GM_setClipboard(inputBox.value, "text");
- showAlert(t("tips"), t("copyDone"), true);
- });
- const btnClear = genBtn(`🗑️${t("reset")}`, t("resetDesc"), () => {
- ShowConfirmDialog("", t("resetConfirm"), t("ok"), t("no")).done(() => {
- inputBox.value = "";
- GM_setValue("fac_cart", "");
- fitInputBox();
- });
- });
- const btnReload = genBtn(`🔃${t("reload")}`, t("reloadDesc"), () => {
- ShowConfirmDialog("", t("reloadConfirm"), t("ok"), t("no")).done(() => {
- const s = GM_getValue("fac_cart") ?? "";
- inputBox.value = s;
- fitInputBox();
- });
- });
- const btnHistory = genBtn(`📜${t("history")}`, t("historyDesc"), () => {
- window.location.href =
- "https://help.steampowered.com/zh-cn/accountdata/ShoppingCartHistory";
- });
- const btnBack = genBtn(`↩️${t("goBack")}`, t("goBackDesc"), () => {
- window.location.href = "https://store.steampowered.com/cart/";
- });
- const btnForget = genBtn(`⚠️${t("clear")}`, t("clearDesc"), () => {
- ShowConfirmDialog("", t("clearConfirm"), t("ok"), t("no")).done(() => {
- deleteAccountCart()
- .then(() => {
- location.reload();
- })
- .catch((err) => {
- console.error(err);
- showAlert("出错", err, false);
- });
- });
- });
- const btnHelp = genBtn(`🔣${t("help")}`, t("helpDesc"), () => {
- const {
- script: { version },
- } = GM_info;
- showAlert(
- `${t("helpTitle")} ${version}`,
- [
- `<p>【🔼${t("import")}】${t("importDesc")}</p>`,
- `<p>【✅${t("onlyOnsale")}】${t("onlyOnsaleDesc")}</p>`,
- `<p>【🔽${t("export")}】${t("exportDesc")}</p>`,
- `<p>【✅${t("onlyOnsale")}】${t("onlyOnsaleDesc2")}</p>`,
- `<p>【📋${t("copy")}】${t("copyDesc")}</p>`,
- `<p>【🗑️${t("reset")}】${t("resetDesc")}。</p>`,
- `<p>【📜${t("history")}】${t("historyDesc")}</p>`,
- `<p>【↩️${t("goBack")}】${t("goBackDesc")}</p>`,
- `<p>【⚠️${t("clear")}】${t("clearDesc")}</p>`,
- `<p>【🔣${t("help")}】${t("helpDesc")}</p>`,
- `<p>【<a href="https://keylol.com/t747892-1-1" target="_blank">发布帖</a>】 【<a href="https://blog.chrxw.com/scripts.html" target="_blank">脚本反馈</a>】 【Developed by <a href="https://steamcommunity.com/id/Chr_" target="_blank">Chr_</a>】</p>`,
- ].join("<br>"),
- true
- );
- });
-
- const btnArea = document.createElement("div");
- btnArea.appendChild(btnImport);
- // btnArea.appendChild(btnImport2);
- btnArea.appendChild(lblDiscount);
- btnArea.appendChild(genSpan(" | "));
- btnArea.appendChild(btnExport);
- btnArea.appendChild(lblDiscount2);
- btnArea.appendChild(genSpan(" | "));
- btnArea.appendChild(btnConvertToGift);
- btnArea.appendChild(btnConvertToSelf);
- btnArea.appendChild(genSpan(" | "));
- btnArea.appendChild(btnHelp);
-
- const btnArea2 = document.createElement("div");
- btnArea2.appendChild(btnCopy);
- btnArea2.appendChild(btnClear);
- btnArea2.appendChild(btnReload);
- btnArea2.appendChild(genSpan(" | "));
- btnArea2.appendChild(histryPage ? btnBack : btnHistory);
- btnArea2.appendChild(genSpan(" | "));
- btnArea2.appendChild(btnForget);
-
- window.addEventListener("beforeunload", () => {
- GM_setValue("fac_cart", inputBox.value);
- GM_setValue("fac_discount", chkDiscount.checked);
- GM_setValue("fac_discount2", chkDiscount2.checked);
- });
-
- //等待购物车加载完毕, 显示额外面板
- const timer = setInterval(() => {
- const continer = document.querySelector("div[data-featuretarget='react-root']>div>div:last-child>div:last-child>div:first-child>div:last-child");
- if (continer) {
- clearInterval(timer);
- continer.appendChild(btnArea);
- continer.appendChild(genBr());
- continer.appendChild(inputBox);
- continer.appendChild(genBr());
- continer.appendChild(btnArea2);
- }
- }, 500);
- }
-
- // getStoreItem([730], null, null).then((data) => console.log(data)).catch(err => console.error(err))
- // getAccountCart().then((data) => console.log(data)).catch(err => console.error(err))
- // addItemsToAccountCart(null, [28627]).then((data) => console.log(data)).catch(err => console.error(err))
-
- //始终在右上角显示购物车按钮
- const cart_btn = document.getElementById("store_header_cart_btn");
- if (cart_btn !== null) {
- cart_btn.style.display = "";
- }
-
- //导入购物车
- function importCart(text, onlyOnSale = false) {
- const regFull = new RegExp(/(app|a|bundle|b|sub|s)\/(\d+)/);
- const regShort = new RegExp(/^([\s]*|)(\d+)/);
-
- return new Promise(async (resolve, reject) => {
- const dialog = showAlert(
- "导入购物车",
- `<h2 id="fac_diag" class="fac_diag">${t("operation")}</h2>`,
- true
- );
-
- const timer = setInterval(async () => {
- let txt = document.getElementById("fac_diag");
- if (txt) {
- clearInterval(timer);
-
- const txts = text.split("\n");
-
- const result = [];
-
- const appIds = [];
- const subIds = [];
- const bundleIds = [];
-
- const targetSubIds = [];
- const targetBundleIds = [];
-
- try {
- txt.textContent = "0/4 开始读取输入信息";
-
- for (let line of txts) {
- if (line.trim() === "") {
- continue;
- }
- const tmp = line.split("#")[0];
-
- const match = line.match(regFull) ?? line.match(regShort);
- if (!match) {
- if (line.search("=====") === -1) {
- result.push(`${tmp} #${t("formatError")}`);
- } else {
- result.push(line);
- }
- continue;
- }
-
- let [_, type, subID] = match;
- subID = parseInt(subID);
- if (subID !== subID) {
- result.push(`${tmp} #${t("formatError")}`);
- continue;
- }
-
- switch (type.toLowerCase()) {
- case "":
- case "a":
- case "app":
- type = "app";
- appIds.push(subID);
- break;
- case "s":
- case "sub":
- type = "sub";
- subIds.push(subID);
- break;
- case "b":
- case "bundle":
- type = "bundle";
- bundleIds.push(subID);
- break;
- default:
- result.push(`${tmp} #${t("formatError")}`);
- continue;
- }
- }
-
- const count = appIds.length + subIds.length + bundleIds.length;
- txt.textContent = `1/4 成功读取 ${count} 个输入内容`;
-
- if (count > 0) {
- txt.textContent = "1/4 开始读取游戏信息";
- const store_items = await getStoreItem(appIds, subIds, bundleIds);
-
- console.log(store_items);
-
- txt.textContent = "2/4 读取游戏信息成功";
-
- for (let { appid, purchase_options } of store_items) {
- if (!purchase_options) { continue; }
-
- //输入值包含AppId, 解析可购买项
- if (appIds.includes(appid)) {
- if (purchase_options.length >= 1) {
- const { packageid, bundleid, purchase_option_name: name, discount_pct: discount, formatted_final_price: price } = purchase_options[0];
- if (discount) {
- if (packageid) {
- result.push(`sub/${packageid} #app/${appid} #${name} 💳 ${price} 🔖 ${discount}`);
- targetSubIds.push(packageid);
- } else if (bundleid) {
- result.push(`bundle/${bundleid} #app/${appid} #${name} 💳 ${price} 🔖 ${discount}`);
- targetBundleIds.push(bundleid);
- }
- } else {
- if (packageid) {
- if (!onlyOnSale) {
- result.push(`sub/${packageid} #app/${appid} #${name} 💳 ${price}`);
- targetSubIds.push(packageid);
- } else {
- result.push(`sub/${packageid} #app/${appid} #排除 #${name} 💳 ${price}`);
- }
- } else if (bundleid) {
- if (!onlyOnSale) {
- result.push(`bundle/${bundleid} #app/${appid} #${name} 💳 ${price}`);
- targetBundleIds.push(bundleid);
- } else {
- result.push(`bundle/${bundleid} #app/${appid} #排除 #${name} 💳 ${price}`);
- }
- }
- }
- } else {
- result.push(`${tmp} #无可购买项目`);
- }
- }
-
- for (let { packageid, bundleid, purchase_option_name: name, discount_pct: discount, formatted_final_price: price } of purchase_options) {
- if (discount) {
- if (packageid && subIds.includes(packageid)) {
- result.push(`sub/${packageid} #${name} 💳 ${price} 🔖 ${discount}`);
- targetSubIds.push(packageid);
- } else if (bundleid && bundleIds.includes(bundleid)) {
- result.push(`bundle/${bundleid} #${name} 💳 ${price} 🔖 ${discount}`);
- targetBundleIds.push(bundleid);
- }
- } else {
- if (packageid && subIds.includes(packageid)) {
- if (!onlyOnSale) {
- result.push(`sub/${packageid} #${name} 💳 ${price}`);
- targetSubIds.push(packageid);
- } else {
- result.push(`sub/${packageid} #排除 #${name} 💳 ${price}`);
-
- }
- } else if (bundleid && bundleIds.includes(bundleid)) {
- if (!onlyOnSale) {
- result.push(`bundle/${bundleid} #${name} 💳 ${price}`);
- targetBundleIds.push(bundleid);
- } else {
- result.push(`bundle/${bundleid} #排除 #${name} 💳 ${price}`);
- }
- }
- }
- }
- }
-
- txt.textContent = "3/4 解析游戏信息成功";
-
- const data = await addItemsToAccountCart(targetSubIds, targetBundleIds, false);
- console.log(data);
-
- txt.textContent = "4/4 导入购物车成功";
-
- dialog.Dismiss();
-
- resolve(result.join("\n"));
-
- setTimeout(() => {
- window.location.reload();
- }, 1000);
-
- } else {
- txt.textContent = "4/4 尚未输入有效内容";
- resolve(result.join("\n"));
- }
-
- } catch (err) {
- txt.textContent = "导出购物车失败";
- console.error(err);
- resolve(result.join("\n"));
- }
- }
- }, 200);
- });
- }
- //导出购物车
- function exportCart(onlyOnsale = false) {
- return new Promise(async (resolve, reject) => {
- const dialog = showAlert(
- "导出购物车",
- `<h2 id="fac_diag" class="fac_diag">${t("operation")}</h2>`,
- true
- );
-
- const timer = setInterval(async () => {
- let txt = document.getElementById("fac_diag");
- if (txt) {
- clearInterval(timer);
-
- const result = [];
-
- const subIds = [];
- const bundleIds = [];
- const gameNames = {};
-
- try {
- txt.textContent = "0/4 开始读取账号购物车";
-
- const { line_items } = await getAccountCart();
-
- if (line_items) {
- for (let { packageid, bundleid } of line_items) {
- if (packageid) {
- subIds.push(packageid);
- }
- else if (bundleid) {
- bundleIds.push(bundleid);
- }
- }
- }
-
- const count = subIds.length + bundleIds.length;
- txt.textContent = `1/4 成功读取 ${count} 个购物车内容`;
-
- if (count > 0) {
- txt.textContent = "1/4 开始读取游戏信息";
- const store_items = await getStoreItem(null, subIds, bundleIds);
- txt.textContent = "2/4 读取游戏信息成功";
-
- for (let { purchase_options } of store_items) {
- if (!purchase_options) { continue; }
-
- for (let { packageid, bundleid, purchase_option_name, discount_pct } of purchase_options) {
- let key;
- if (packageid) {
- key = `sub/${packageid}`;
- } else if (bundleid) {
- key = `bundle/${bundleid}`;
- }
- else {
- continue;
- }
- gameNames[key] = [`${purchase_option_name}`, discount_pct];
- }
- }
-
- txt.textContent = "3/4 解析游戏信息成功";
- txt.textContent = "3/4 开始导出购物车信息";
- if (line_items) {
- for (let { packageid, bundleid, price_when_added: { formatted_amount } } of line_items) {
- let key;
- if (packageid) {
- key = `sub/${packageid}`;
- } else if (bundleid) {
- key = `bundle/${bundleid}`;
- }
- const [name, discount] = gameNames[key] ?? "_";
- if (discount) {
- result.push(`${key} #${name} 💳 ${formatted_amount} 🔖 ${discount}`);
- } else if (!onlyOnsale) {
- result.push(`${key} #${name} 💳 ${formatted_amount}`);
- }
- }
- }
-
- txt.textContent = "3/4 导出购物车信息成功";
- dialog.Dismiss();
-
- resolve(result.join("\n"));
-
- } else {
- txt.textContent = "4/4 购物车内容为空";
- resolve(result.join("\n"));
- }
-
- } catch (err) {
- txt.textContent = "读取账号购物车失败";
- console.error(err);
- resolve(result.join("\n"));
- }
- }
- }, 200);
- });
- }
-
- //编辑购物车
- async function editCart(setToGift = false, setToPrivate = false) {
- const setGiftInfo = await inputGiftee(setToGift);
-
- return new Promise(async (resolve, reject) => {
- const dialog = showAlert(
- "编辑购物车",
- `<h2 id="fac_diag" class="fac_diag">${t("operation")}</h2>`,
- true
- );
-
- const timer = setInterval(async () => {
- let txt = document.getElementById("fac_diag");
- if (txt) {
- clearInterval(timer);
-
- const lineItemIds = [];
-
- try {
- txt.textContent = "0/3 开始读取账号购物车";
-
- const { line_items } = await getAccountCart();
-
- if (line_items) {
- for (const { line_item_id, flags: { is_gift, is_private }, gift_info } of line_items) {
- const accountid_giftee = gift_info?.accountid_giftee?.toString() ?? "";
-
- console.log(line_item_id, is_gift, is_private, accountid_giftee);
-
- //跳过无需处理的id
- if (setToGift) {
- if (is_gift && setGiftInfo?.accountid_giftee == accountid_giftee) {
- continue;
- }
- } else if (setToPrivate) {
- if (is_private) {
- continue;
- }
- }
- else {
- if (!is_gift && !is_private) {
- continue;
- }
- }
-
- lineItemIds.push(line_item_id);
- }
- }
-
- console.log(lineItemIds);
-
- const count = lineItemIds.length;
- txt.textContent = `1/3 共计 ${count} 个待修改购物车内容`;
-
- if (count > 0) {
- for (let i = 0; i < count; i++) {
- const itemId = lineItemIds[i];
- await editAccountCart(itemId, setToGift, setToPrivate, setGiftInfo);
- txt.textContent = `2/3 当前进度 ${i + 1} / ${count}`;
- }
-
- txt.textContent = "3/3 批量修改购物车成功";
-
- setTimeout(() => dialog.Dismiss(), 1000);
- resolve();
- } else {
- txt.textContent = "3/3 购物车无需修改";
-
- setTimeout(() => dialog.Dismiss(), 1000);
- resolve();
- }
-
- } catch (err) {
- txt.textContent = "读取账号购物车失败";
- console.error(err);
- resolve();
- }
- }
- }, 200);
- });
- }
-
- const steamIdConvert = BigInt("0x110000100000000");
-
- function inputGiftee(isGift = false) {
- const nickname = document.querySelector("#account_pulldown")?.textContent?.trim() ?? "unknown";
-
- return new Promise((resolve, reject) => {
- if (!isGift) {
- resolve(null);
- } else {
- ShowPromptDialog("提示", "请输入礼物接收人的 SteamID 或者好友代码, 可以留空", "确认", "跳过")
- .done(text => {
- try {
- let steamId = BigInt(text.trim());
- if (steamId == 0) {
- throw "输入数值有误";
- }
-
- if (steamId > steamIdConvert) {
- steamId -= steamIdConvert;
- }
- const giftInfo = {
- accountid_giftee: steamId.toString(),
- gift_message: {
- gifteename: steamId.toString(),
- message: "Send by Fast_Add_Cart",
- sentiment: nickname,
- signature: nickname
- },
- time_scheduled_send: 0
- };
- resolve(giftInfo);
- } catch (err) {
- ShowAlertDialog("提示", "输入数值有误")
- .then(() => resolve(null));
- }
- })
- .fail(() => {
- resolve(null);
- });
- }
- });
- }
-
- // 获取游戏详情
- function getStoreItem(appIds = null, subIds = null, bundleIds = null) {
- return new Promise((resolve, reject) => {
- const ids = [];
- if (appIds) {
- for (let x of appIds) {
- ids.push({ appid: x });
- }
- }
- if (subIds) {
- for (let x of subIds) {
- ids.push({ packageid: x });
- }
- }
- if (bundleIds) {
- for (let x of bundleIds) {
- ids.push({ bundleid: x });
- }
- }
-
- if (ids.length === 0) {
- reject([false, "未提供有效ID"]);
- }
-
- const payload = {
- ids,
- context: {
- language: storeLanguage,
- country_code: userCountry,
- steam_realm: "1"
- },
- data_request: {
- include_all_purchase_options: true
- }
- };
- const json = encodeURI(JSON.stringify(payload));
- fetch(
- `https://api.steampowered.com/IStoreBrowseService/GetItems/v1/?input_json=${json}`,
- {
- method: "GET",
- }
- )
- .then(async (response) => {
- if (response.ok) {
- const { response: { store_items } } = await response.json();
- resolve(store_items);
- } else {
- reject(t("networkRequestError"));
- }
- })
- .catch((err) => {
- reject(err);
- });
- });
- }
-
- //读取购物车
- function getAccountCart() {
- return new Promise((resolve, reject) => {
- fetch(
- `https://api.steampowered.com/IAccountCartService/GetCart/v1/?access_token=${accessToken}`,
- {
- method: "GET",
- }
- )
- .then(async (response) => {
- if (response.ok) {
- const { response: { cart } } = await response.json();
- resolve(cart);
- } else {
- reject(t("networkRequestError"));
- }
- })
- .catch((err) => {
- reject(err);
- });
- });
- }
-
- //添加购物车
- function addItemsToAccountCart(subIds = null, bundleIds = null, isPrivate = false) {
- return new Promise((resolve, reject) => {
- const items = [];
- if (subIds) {
- for (let x of subIds) {
- items.push({ packageid: x });
- }
- }
- if (bundleIds) {
- for (let x of bundleIds) {
- items.push({ bundleid: x });
- }
- }
- if (items.length === 0) {
- reject([false, "未提供有效ID"]);
- }
-
- for (let x of items) {
- x["gift_info"] = null; //giftInfo;
- x["flags"] = {
- is_gift: false,
- is_private: isPrivate == true
- };
- }
-
- const payload = {
- user_country: userCountry,
- items,
- navdata: {
- domain: "store.steampowered.com",
- controller: "default",
- method: "default",
- submethod: "",
- feature: "spotlight",
- depth: 1,
- countrycode: userCountry,
- webkey: 0,
- is_client: false,
- curator_data: {
- clanid: null,
- listid: null
- },
- is_likely_bot: false,
- is_utm: false
- }
- };
- const json = JSON.stringify(payload);
-
- fetch(
- `https://api.steampowered.com/IAccountCartService/AddItemsToCart/v1/?access_token=${accessToken}`,
- {
- method: "POST",
- body: `input_json=${json}`,
- headers: {
- "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
- },
- }
- )
- .then(async (response) => {
- if (response.ok) {
- const { response: { cart } } = await response.json();
- resolve(cart);
- } else {
- reject(t("networkRequestError"));
- }
- })
- .catch((err) => {
- reject(err);
- });
- });
- }
-
- //编辑购物车
- function editAccountCart(itemId, isGift, isPrivate, giftInfo = null) {
- return new Promise((resolve, reject) => {
- const payload = {
- line_item_id: itemId,
- user_country: userCountry,
- gift_info: giftInfo,
- flags: {
- is_gift: isGift,
- is_private: isPrivate,
- }
- };
- const json = JSON.stringify(payload);
-
- console.log(json);
-
- fetch(
- `https://api.steampowered.com/IAccountCartService/ModifyLineItem/v1/?access_token=${accessToken}`,
- {
- method: "POST",
- body: `input_json=${json}`,
- headers: {
- "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
- },
- }
- )
- .then(async (response) => {
- if (response.ok) {
- const { response: { line_item_ids, cart } } = await response.json();
- resolve([line_item_ids, cart]);
- } else {
- reject(t("networkRequestError"));
- }
- })
- .catch((err) => {
- reject(err);
- });
- });
- }
-
- //删除购物车
- function deleteAccountCart() {
- return new Promise((resolve, reject) => {
- fetch(
- `https://api.steampowered.com/IAccountCartService/DeleteCart/v1/?access_token=${accessToken}`,
- {
- method: "POST",
- }
- )
- .then(async (response) => {
- if (response.ok) {
- const { response: { line_item_ids, cart } } = await response.json();
- resolve([line_item_ids, cart]);
- } else {
- reject(t("networkRequestError"));
- }
- })
- .catch((err) => {
- reject(err);
- });
- });
- }
-
- //显示提示
- function showAlert(title, text, succ = true) {
- return ShowAlertDialog(`${succ ? "✅" : "❌"}${title}`, text);
- }
- })();
-
- GM_addStyle(`
- button.fac_listbtns {
- display: none;
- position: relative;
- z-index: 100;
- padding: 1px;
- }
- a.search_result_row > button.fac_listbtns {
- top: -25px;
- left: 300px;
- }
- a.tab_item > button.fac_listbtns {
- top: -40px;
- left: 330px;
- }
- a.recommendation_link > button.fac_listbtns {
- bottom: 10px;
- right: 10px;
- position: absolute;
- }
- div.wishlist_row > button.fac_listbtns {
- top: 35%;
- right: 30%;
- position: absolute;
- }
- div.game_purchase_action > button.fac_listbtns {
- right: 8px;
- bottom: 8px;
- }
- button.fac_cartbtns {
- padding: 5px 8px;
- }
- button.fac_cartbtns:not(:last-child) {
- margin-right: 4px;
- }
- button.fac_cartbtns:not(:first-child) {
- margin-left: 4px;
- }
- a.tab_item:hover button.fac_listbtns,
- a.search_result_row:hover button.fac_listbtns,
- div.recommendation:hover button.fac_listbtns,
- div.wishlist_row:hover button.fac_listbtns {
- display: block;
- }
- div.game_purchase_action:hover > button.fac_listbtns {
- display: inline;
- }
- button.fac_choose {
- padding: 1px;
- margin: 2px 5px;
- }
- textarea.fac_inputbox {
- resize: vertical;
- font-size: 12px;
- min-height: 130px;
- width: 98%;
- background: #3d4450;
- color: #fff;
- padding: 1%;
- border: gray;
- border-radius: 5px;
- }
- `);