// ==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/
// ==/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();