Add to cart without redirect to cart page, also provide import/export cart feature.
// ==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: "格式有误", choos###b: "请选择SUB", operation: "操作中……", operationDone: "操作完成", addCart: "添加购物车", addCartTips: "添加到购物车……", addCartErrorSubNotFount: "未识别到SubID", noSubDesc: "可能尚未发行或者是免费游戏", inCart: "在购物车中", importingTitle: "正在导入购物车……", add: "添加", toCart: "到购物车", tips: "提示", ok: "是", no: "否", fetchingSubs: "读取可用SUB", noSubFound: "未找到可用SUB", networkError: "网络错误", addCartSuccess: "添加购物车成功", addCartError: "添加购物车失败", networkRequestError: "网络请求失败", unknownError: "未知错误", unrecognizedR###lt: "返回了未知结果", 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", choos###b: "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", unrecognizedR###lt: "Returned unrecognized r###lt", 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 r###lt = []; 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) { r###lt.push(`${tmp} #${t("formatError")}`); } else { r###lt.push(line); } continue; } let [_, type, subID] = match; subID = parseInt(subID); if (subID !== subID) { r###lt.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: r###lt.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) { r###lt.push(`sub/${packageid} #app/${appid} #${name} 💳 ${price} 🔖 ${discount}`); targetSubIds.push(packageid); } else if (bundleid) { r###lt.push(`bundle/${bundleid} #app/${appid} #${name} 💳 ${price} 🔖 ${discount}`); targetBundleIds.push(bundleid); } } else { if (packageid) { if (!onlyOnSale) { r###lt.push(`sub/${packageid} #app/${appid} #${name} 💳 ${price}`); targetSubIds.push(packageid); } else { r###lt.push(`sub/${packageid} #app/${appid} #排除 #${name} 💳 ${price}`); } } else if (bundleid) { if (!onlyOnSale) { r###lt.push(`bundle/${bundleid} #app/${appid} #${name} 💳 ${price}`); targetBundleIds.push(bundleid); } else { r###lt.push(`bundle/${bundleid} #app/${appid} #排除 #${name} 💳 ${price}`); } } } } else { r###lt.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)) { r###lt.push(`sub/${packageid} #${name} 💳 ${price} 🔖 ${discount}`); targetSubIds.push(packageid); } else if (bundleid && bundleIds.includes(bundleid)) { r###lt.push(`bundle/${bundleid} #${name} 💳 ${price} 🔖 ${discount}`); targetBundleIds.push(bundleid); } } else { if (packageid && subIds.includes(packageid)) { if (!onlyOnSale) { r###lt.push(`sub/${packageid} #${name} 💳 ${price}`); targetSubIds.push(packageid); } else { r###lt.push(`sub/${packageid} #排除 #${name} 💳 ${price}`); } } else if (bundleid && bundleIds.includes(bundleid)) { if (!onlyOnSale) { r###lt.push(`bundle/${bundleid} #${name} 💳 ${price}`); targetBundleIds.push(bundleid); } else { r###lt.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(r###lt.join("\n")); setTimeout(() => { window.location.reload(); }, 1000); } else { txt.textContent = "4/4 尚未输入有效内容"; resolve(r###lt.join("\n")); } } catch (err) { txt.textContent = "导出购物车失败"; console.error(err); resolve(r###lt.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 r###lt = []; 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) { r###lt.push(`${key} #${name} 💳 ${formatted_amount} 🔖 ${discount}`); } else if (!onlyOnsale) { r###lt.push(`${key} #${name} 💳 ${formatted_amount}`); } } } txt.textContent = "3/4 导出购物车信息成功"; dialog.Dismiss(); resolve(r###lt.join("\n")); } else { txt.textContent = "4/4 购物车内容为空"; resolve(r###lt.join("\n")); } } catch (err) { txt.textContent = "读取账号购物车失败"; console.error(err); resolve(r###lt.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_r###lt_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_r###lt_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; } `);