// ==UserScript== // @name steam补充包工具 // @namespace http://tampermonkey.net/ // @version 1.2 // @description To dear sbeamer! // @author 逍遥千寻 // @match https://steamcommunity.com//tradingcards/boostercreator/ // @include *steamcommunity.com/*boostercreator* // @icon https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/f4/f41c579d01a4e5fa0f4f91d69cb93896b8478ccf_medium.jpg // steamcn https://steamcn.com/suid-457526 // steam https://steamcommunity.com/id/zhangxuerui/ // @downloadURL none // ==/UserScript== //获取cookie,组装存储key const cookie = document.cookie; const boosterKey = 'steam_booster' const sessionId = cookie.split('sessionid=')[1].split(';')[0]; //游戏选择器 let gameSelector = document.getElementById("booster_game_selector"); //下方展示部分 let gameForm = document.getElementsByClassName('booster_creator_right')[0] //所有游戏 let backUpOptions = [] //已收藏游戏 let filterOptions = [] ///未收藏游戏 let outOptions = [] //当前可以做补充包的游戏,用于一键做包,只会操作收藏的游戏 let availableGame = [] let doneList = [] //游戏管理页面 let searchInfo = '' let typeIndex = 0 let searchResult = []; let pageNum = 1; let totalCount = 0; const pageSize = 10 let priceData = {} //该用户存储的补充包信息 let boosterInfo = {game: [], filter: true} //市场宝珠价格 let gemsSackPrice = 0 //所有游戏的信息,包括appid/name/price/series/available_at_time const GAME_INFO = CBoosterCreatorPage.sm_rgBoosterData const classObj = { disableButton:'btnv6_blue_blue_innerfade btn_medium btn_makepack btn_disabled', enableButton:'btnv6_blue_blue_innerfade btn_medium btn_makepack', } function getGemsSackPrice() { if(gemsSackPrice > 0){ return } $J.get('https://steamcommunity.com/market/listings/753/753-Sack%20of%20Gems',function (data) { let nameid = data.match(/Market_LoadOrderSpread\( (\d+)/)[1]; let currency = parseInt(data.match(/"wallet_currency":(\d+)/)[1]); let language = data.match(/g_strLanguage = "([^"]+)"/)[1]; let country = data.match(/g_strCountryCode = "([^"]+)"/)[1]; $J.ajax({ url: 'https://steamcommunity.com/market/itemordershistogram', type: 'GET', data: { country: country, language: language, currency: currency, item_nameid: nameid } }).success(function (price) { let sellOrder = price.sell_order_graph; if(sellOrder && sellOrder.length >= 0){ gemsSackPrice = sellOrder[0]['0'] } }).error(function () { }) }) } //查询当前页面的补充包的价格 function getBoosterPrice(gameList) { if(!gameList || gameList.length === 0){ return } for (let i = 0; i < gameList.length; i++) { let item = GAME_INFO[gameList[i]] if(priceData[item.appid]){ continue } let priceInfo = { sellPrice:'未知', buyPrice:'未知' } $J.get('https://steamcommunity.com/market/listings/753/'+item.appid+'-'+encodeURIComponent(item.name)+'%20Booster%20Pack',function (data) { let nameid = data.match(/Market_LoadOrderSpread\( (\d+)/)[1]; let currency = parseInt(data.match(/"wallet_currency":(\d+)/)[1]); let language = data.match(/g_strLanguage = "([^"]+)"/)[1]; let country = data.match(/g_strCountryCode = "([^"]+)"/)[1]; $J.ajax({ url: 'https://steamcommunity.com/market/itemordershistogram', type: 'GET', data: { country: country, language: language, currency: currency, item_nameid: nameid } }).success(function (price) { let sellOrder = price.sell_order_graph; let buyOrder = price.buy_order_graph; if(buyOrder && buyOrder.length >= 0){ priceInfo.buyPrice = buyOrder[0]['0'] } if(sellOrder && sellOrder.length >= 0){ priceInfo.sellPrice = sellOrder[0]['0'] } generateCreateButton() generateGameList(pageNum, pageSize, searchResult) }).error(function () { generateCreateButton() generateGameList(pageNum, pageSize, searchResult) }) }) priceData[item.appid] = priceInfo } } //循环制作补充包 function createBooster(index) { document.getElementById("createButton").innerHTML = "正在制作" let item = availableGame[index] $J.ajax({ url: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/', type: 'POST', data: { sessionid: sessionId, appid: item.value, series: GAME_INFO[item.value].series, tradability_preference: 1 }, crossDomain: true, xhrFields: {withCredentials: true} }).success(function () { doneList.push(item.value) if (index + 1 < availableGame.length) { createBooster(index + 1) } else { setUnavalilable() buildFilterAndAvailable() generateCreateButton() } }).error(function () { document.getElementById("createButton").innerHTML = "宝石不足或其他原因" }); } //制包成功后设置为unavalilable function setUnavalilable() { if(doneList && doneList.length > 0){ for (let i = 0; i < backUpOptions.length; i++) { if (doneList.indexOf(backUpOptions[i].value) > -1) { backUpOptions[i].setAttribute("class", "unavailable") } } } } //判断字符串是否为空,不为空是true,为空是false function stringNotBlank(value) { if (!value || value.trim() === '') { return false; } else { return true; } } //判断字符串是否为空,为空是true,不为空是false function stringBlank(value) { if (!value || value.trim() === '') { return true; } else { return false; } } //执行搜索 function doSearch() { let inputValue = document.getElementById('searchInput').value let typeSelect = document.getElementById('typeSelect') if (stringNotBlank(inputValue)) { searchInfo = inputValue.trim() } else { searchInfo = '' } typeIndex = typeSelect.selectedIndex searchResult = [] let tempGameInfo = {} if(typeIndex === 0){ backUpOptions.map(function (item) { tempGameInfo[item.value] = GAME_INFO[item.value] }) }else if(typeIndex === 1){ filterOptions.map(function (item) { tempGameInfo[item.value] = GAME_INFO[item.value] }) }else if(typeIndex === 2){ outOptions.map(function (item) { tempGameInfo[item.value] = GAME_INFO[item.value] }) } if (stringBlank(searchInfo)) { for (let key in tempGameInfo) { searchResult.push(tempGameInfo[key]) } } else { //支持大小写不敏感匹配、appid匹配、多个字符串匹配 for (let key in tempGameInfo) { if (searchInfo.indexOf(' ') !== -1) { let match = true let keyWordArray = searchInfo.split(' ') for (let i = 0; i < keyWordArray.length; i++) { if (tempGameInfo[key].name.toUpperCase().indexOf(keyWordArray[i].trim().toUpperCase()) === -1) { match = false break } } if (match) { searchResult.push(tempGameInfo[key]) } } else if (searchInfo === tempGameInfo[key].appid || tempGameInfo[key].name.toUpperCase().indexOf(searchInfo.toUpperCase()) > -1) { searchResult.push(tempGameInfo[key]) } } } pageNum = 1 generateGameList(pageNum, pageSize, searchResult) } //构建市场搜索链接 function buildMarketUrl(name) { if(stringBlank(name)){ return 'https://store.steampowered.com/' } else { let searchUrl = 'https://steamcommunity.com/market/search?q=' let keyArray = name.split(' ') searchUrl += keyArray[0] for (let i = 1; i < keyArray.length; i++) { searchUrl = searchUrl + '+' + keyArray[i] } return searchUrl } } //生成游戏列表 function generateGameList(pageNum, pageSize, searchResult) { //重新生成,删除旧数据 for (let i = gameForm.childNodes.length - 1; i >= 0; i--) { gameForm.removeChild(gameForm.childNodes[i]); } gameForm.setAttribute('style', 'width:750px') //搜索输入框 let searchInput = document.createElement('input'); searchInput.setAttribute('id', 'searchInput') searchInput.setAttribute('style', 'background-color: rgba( 103, 193, 245, 0.2 );\n' + ' color: #fff;\n' + ' border: 1px solid #000;\n' + ' border-radius: 3px;\n' + ' box-shadow: 1px 1px 0px rgba( 103, 193, 245, 0.15 );\n' + ' width: 240px;\n' + ' padding: 5px;\n' + ' max-width: calc( 100% - 80px - 2px );') if (searchInfo && searchInfo.trim() !== '') { searchInput.value = searchInfo } //搜索类型选择 let typeSelect = document.createElement('select') let allOption = document.createElement('option') allOption.setAttribute('value','all') allOption.innerHTML = '全部' let filterOption = document.createElement('option') filterOption.setAttribute('value','filter') filterOption.innerHTML = '已收藏' let outOption = document.createElement('option') outOption.setAttribute('value','out') outOption.innerHTML = '未收藏' typeSelect.add(allOption) typeSelect.add(filterOption) typeSelect.add(outOption) typeSelect.selectedIndex = typeIndex typeSelect.setAttribute('style','margin-left:30px;width:100px') typeSelect.setAttribute('id','typeSelect') //搜索按钮 let searchButton = document.createElement('button') searchButton.setAttribute('id', 'searchButton') searchButton.innerHTML = '搜索' searchButton.setAttribute('style', ' border-radius: 2px;\n' + ' border: none;\n' + ' padding: 1px;\n' + ' display: inline-block;\n' + ' cursor: pointer;\n' + ' text-decoration: none !important;\n' + ' color: #67c1f5 !important;\n' + ' background: rgba( 103, 193, 245, 0.2 );\n' + ' height: 26px;\n' + ' width: 100px;\n' + ' float: right;\n' + ' margin-bottom: 32px;') searchButton.onclick = function () { doSearch() } gameForm.appendChild(searchInput) gameForm.appendChild(typeSelect) gameForm.appendChild(searchButton) let startIndex = (pageNum - 1) * pageSize if (startIndex > searchResult.length - 1) { return null; } let table = document.createElement('table') table.setAttribute('style', 'width:100%') let gameList = [] for (let i = startIndex; i < searchResult.length && i < startIndex + pageSize; i++) { let item = searchResult[i] let tr = document.createElement('tr'); tr.setAttribute('style', 'height:60px') let img = document.createElement('img'); img.setAttribute('src', 'https://steamcdn-a.akamaihd.net/steam/apps/' + item.appid + '/capsule_sm_120.jpg') let aTag = document.createElement('a'); aTag.setAttribute('href', buildMarketUrl(item.name)) aTag.setAttribute('target', '_blank') aTag.appendChild(img) let name = document.createElement('span') name.innerHTML = item.name name.setAttribute('style', 'display: inline-block;overflow: hidden;text-overflow: ellipsis;width: 320px;white-space: nowrap; margin-left: 16px;') name.setAttribute('title', item.name) let price = document.createElement('span') price.innerHTML = item['price'] price.setAttribute('style', 'display: inline-block;width: 50px; margin-left: 16px;') price.setAttribute('title','制作补充包需要的宝珠数') let cost = document.createElement('span') cost.setAttribute('style', 'display: inline-block;width: 30px; margin-left: 12px;') cost.setAttribute('title','此补充包制作成本,取当前市场宝珠最低售价计算') let costPrice = 0.00 if (gemsSackPrice > 0) { let tempCost = item['price'] / 1000 * gemsSackPrice if (!isNaN(tempCost)) { costPrice = parseFloat(tempCost.toFixed(2)) cost.innerHTML = costPrice.toString() } } let sellPrice = document.createElement('span') sellPrice.setAttribute('style', 'display: inline-block;width: 30px; margin-left: 12px;') sellPrice.setAttribute('title','市场最低售价') let buyPrice = document.createElement('span') buyPrice.setAttribute('style', 'display: inline-block;width: 30px; margin-left: 12px;') buyPrice.setAttribute('title','市场最高买价,如果高于成本,会展示黄色') if (priceData[item.appid]) { sellPrice.innerHTML = priceData[item.appid].sellPrice let tempBuyPrice = priceData[item.appid].buyPrice buyPrice.innerHTML = tempBuyPrice if (costPrice > 0 && !isNaN(tempBuyPrice)) { if (costPrice < tempBuyPrice) { buyPrice.setAttribute('style', 'display: inline-block;width: 30px; margin-left: 12px;color:yellow') } } } else { gameList.push(item.appid) } //收藏和移除按钮 let button = document.createElement('button') button.innerHTML = boosterInfo.game.indexOf(item.appid.toString()) < 0 ? '收藏' : '移除' button.setAttribute('class', classObj.enableButton) button.setAttribute('style', boosterInfo.game.indexOf(item.appid.toString()) < 0 ? 'height:26px;width:60px;float:right;margin-top:14px' : 'height:26px;width:60px;float:right;margin-top:14px;background:rebeccapurple') button.setAttribute('id', item.appid.toString()) button.onclick = boosterInfo.game.indexOf(item.appid.toString()) < 0 ? function () { saveItem(item.appid.toString()) } : function () { deleteItem(item.appid.toString()) } tr.appendChild(aTag); tr.appendChild(name) tr.appendChild(price) tr.appendChild(cost) tr.appendChild(sellPrice) tr.appendChild(buyPrice) tr.appendChild(button) table.appendChild(tr) } gameForm.appendChild(table) //计算页数 totalCount = Math.ceil(searchResult.length / pageSize) //上一页按钮 let beforeButton = document.createElement('button'); beforeButton.setAttribute('id', 'beforeButton') beforeButton.innerHTML = '上一页' beforeButton.setAttribute('class',pageNum === 1 ? classObj.disableButton:classObj.enableButton) beforeButton.setAttribute('style', 'height: 25px;margin-right: 30px;width: 80px;') beforeButton.onclick = function () { beforePage() } gameForm.appendChild(beforeButton) let pageSpan = document.createElement('span'); pageSpan.innerHTML = '当前第 ' + pageNum + ' 页,共 ' + totalCount + ' 页' gameForm.appendChild(pageSpan) //下一页按钮 let afterButton = document.createElement('button'); afterButton.setAttribute('id', 'afterButton') afterButton.innerHTML = '下一页' afterButton.setAttribute('class',pageNum === totalCount ? classObj.disableButton:classObj.enableButton) afterButton.setAttribute('style', 'height: 25px;margin-left: 30px;width: 80px;') afterButton.onclick = function () { afterPage() } gameForm.appendChild(afterButton) //跳转页输入 let jumpInput = document.createElement('input'); jumpInput.setAttribute('id', 'jumpInput') jumpInput.setAttribute('style', 'background-color: rgba( 103, 193, 245, 0.2 );\n' + ' color: #fff;\n' + ' border: 1px solid #000;\n' + ' border-radius: 3px;\n' + ' box-shadow: 1px 1px 0px rgba( 103, 193, 245, 0.15 );\n' + ' width: 60px;\n' + ' padding: 5px;\n' + ' margin-left: 30px;\n' + ' max-width: calc( 100% - 80px - 2px );') gameForm.appendChild(jumpInput) //跳转按钮 let jumpButton = document.createElement('button'); jumpButton.setAttribute('id', 'jumpButton') jumpButton.innerHTML = '跳转' jumpButton.setAttribute('class',classObj.enableButton) jumpButton.setAttribute('style', 'height: 25px;margin-left: 30px;width: 80px;') jumpButton.onclick = function () { jumpPage() } gameForm.appendChild(jumpButton) if(gameList.length > 0){ getBoosterPrice(gameList) } } //跳转到指定页 function jumpPage() { let jumpNum = document.getElementById('jumpInput').value if(isNaN(jumpNum) || stringBlank(jumpNum) || parseInt(jumpNum) < 1){ return } pageNum = parseInt(jumpNum) if (pageNum > totalCount) { pageNum = 1 } document.getElementById('jumpInput').value ='' generateGameList(pageNum,pageSize,searchResult) } //上一页 function beforePage() { if (pageNum < 2) { return } pageNum = pageNum - 1 generateGameList(pageNum, pageSize, searchResult) } //下一页 function afterPage() { if (pageNum > totalCount - 1) { return } pageNum = pageNum + 1 generateGameList(pageNum, pageSize, searchResult) } //生成一键做包等 function generateCreateButton() { //每次删除后重新创建 let tempCreate = document.getElementById('createButton') if (tempCreate !== null) { tempCreate.parentNode.removeChild(tempCreate) } let tempConvert = document.getElementById('convertButton') if (tempConvert !== null) { tempConvert.parentNode.removeChild(tempConvert) } //更新下拉列表 gameSelector.options.length = 0; if (boosterInfo.filter) { filterOptions.map((item) => { gameSelector.add(item) }) } else { backUpOptions.map((item) => { gameSelector.add(item) }) } //绘制创建按钮 let createButton = document.createElement('button') createButton.setAttribute('id', 'createButton') createButton.onclick = function () { document.getElementById("createButton").setAttribute('class', classObj.disableButton) doneList = [] createBooster(0) } if (availableGame.length === 0) { createButton.innerHTML = '收藏全部冷却中' createButton.setAttribute('class', classObj.disableButton) } else { createButton.innerHTML = '一键制作 ' + availableGame.length + ' 个补充包' createButton.setAttribute('class', classObj.enableButton) } createButton.setAttribute('style', 'height: 29px; margin-top: 16px;width: 178px;') document.getElementsByClassName('booster_game_selector')[0].appendChild(createButton) //绘制转换按钮 let convertButton = document.createElement('button') convertButton.setAttribute('id', 'convertButton') convertButton.setAttribute('class', classObj.enableButton) convertButton.innerHTML = boosterInfo.filter ? '展示全部' : '展示收藏' convertButton.setAttribute('style', 'height: 29px; margin-top: 16px;width: 100px;margin-left:22px') convertButton.onclick = function () { boosterInfo.filter = !boosterInfo.filter saveStorage(boosterKey, boosterInfo) generateCreateButton() } document.getElementsByClassName('booster_game_selector')[0].appendChild(convertButton) } //收藏和移除操作时,重新构建下拉数据 function buildFilterAndAvailable() { filterOptions = [] availableGame = [] outOptions = [] for (let i = 0; i < backUpOptions.length; i++) { let item = backUpOptions[i] if (item.value && boosterInfo.game.indexOf(item.value) > -1) { filterOptions.push(item) if (item.getAttribute("class") === "available") { availableGame.push(item) } }else { outOptions.push(item) } } } //新增游戏收藏 function saveItem(appid) { if (boosterInfo.game.indexOf(appid) > -1) { return } boosterInfo.game.push(appid) saveStorage(boosterKey, boosterInfo) //刷新下拉列表,重新生成按钮和表单 buildFilterAndAvailable() generateCreateButton() generateGameList(pageNum, pageSize, searchResult) } //移除游戏收藏 function deleteItem(appid) { if (boosterInfo.game.indexOf(appid) === -1) { return } boosterInfo.game.splice(boosterInfo.game.indexOf(appid), 1) saveStorage(boosterKey, boosterInfo) //刷新下拉列表,重新生成按钮和表单 buildFilterAndAvailable() generateCreateButton() generateGameList(pageNum, pageSize, searchResult) } //从localStorage取值 function getStorage(key) { return JSON.parse(localStorage.getItem(key)) } //将值存入从localStorage function saveStorage(key, item) { localStorage.setItem(key, JSON.stringify(item)) } //初始化数据 function init() { //没有可以做补充包的游戏,直接返回 if (!gameSelector || gameSelector.length === 0) { return } //查询宝珠价格,用于计算成本 getGemsSackPrice() //删除默认展示 for (let i = gameForm.childNodes.length - 1; i >= 0; i--) { gameForm.removeChild(gameForm.childNodes[i]); } //删除下拉列表第一个‘请选择’ if (stringBlank(gameSelector.options[0].value)) { gameSelector.options.remove(0) } //从localStorage取用户自定义值,默认为空 boosterInfo = getStorage(boosterKey) if (!boosterInfo) { boosterInfo = {game: [], filter: true} } //默认将所有信息加入搜索结果 for (let key in GAME_INFO) { searchResult.push(GAME_INFO[key]) } //遍历每个游戏,分别放入 backUpOptions、filterOptions、availableGame for (let i = 0; i < gameSelector.length; i++) { let item = gameSelector.options[i]; backUpOptions.push(item) if (item.value && boosterInfo.game.indexOf(item.value) > -1) { filterOptions.push(item) if (item.getAttribute("class") === "available") { availableGame.push(item) } }else { outOptions.push(item) } } //生成一键制作补充包等按钮 generateCreateButton() //生成下部游戏列表展示 generateGameList(pageNum, pageSize, searchResult) } //执行初始化工作 init();