steam补充包工具

To dear sbeamer!

  1. // ==UserScript==
  2. // @name steam补充包工具
  3. // @namespace http://tampermonkey.net/
  4. // @version 4.02
  5. // @description To dear sbeamer!
  6. // @author 逍遥千寻
  7. // @include http*://steamcommunity.com/*
  8. // @include http*://store.steampowered.com/*
  9. // @icon https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/f4/f41c579d01a4e5fa0f4f91d69cb93896b8478ccf_medium.jpg
  10. // @grant GM_xmlhttpRequest
  11. // steamcn https://steamcn.com/suid-457526
  12. // steam https://steamcommunity.com/id/zhangxuerui/
  13. // @connect *
  14. // ==/UserScript==
  15.  
  16. //页面定位:默认 = 1;补充包 = 2 ;app详情 = 3 ; 软锁游戏 = 4 ; 搜索卡牌页面 = 5
  17. let pageLocation = 1;
  18. let currentAppId;
  19. let currentAppName;
  20. //获取cookie,组装存储key
  21. const cookie = document.cookie;
  22. //存储补充包游戏列表信息
  23. const boosterKey = 'steam_booster';
  24. //缓存卡牌名称对应的信息,可以减少发送请求数量
  25. const cacheKey = 'steam_booster_card_cache';
  26. const sessionId = cookie.split('sessionid=')[1].split(';')[0];
  27. //整个补充包div
  28. let boosterPage = document.getElementsByClassName("booster_creator_area")[0];
  29. //游戏选择器
  30. let gameSelector = document.getElementById("booster_game_selector");
  31. //下方展示部分
  32. let gameForm = document.getElementsByClassName('booster_creator_right')[0];
  33. //游戏详情页
  34. let gameDes = document.getElementsByClassName("leftcol game_description_column")[0];
  35. //是否正在渲染
  36. let drawing = false;
  37. //是否正在查询卡牌
  38. let queryCard = false;
  39.  
  40. //所有游戏
  41. let backUpOptions = [];
  42. //已收藏游戏
  43. let collectOptions = [];
  44. ///未收藏游戏
  45. let outOptions = [];
  46. //补充包队列
  47. let boosterOptions = [];
  48. //黑名单游戏
  49. let blackOptions = [];
  50. //当前可以做补充包的游戏,用于一键做包,只会操作补充包队列的游戏
  51. let availableGame = [];
  52. let doneList = [];
  53. //可以直接根据appid搜索物品信息的游戏
  54. let enableSearchAppList = [];
  55. //游戏管理页面
  56. let searchInfo = '';
  57. let searchResult = [];
  58. let pageNum = 1;
  59. let totalCount = 0;
  60. let pageSize = 10;
  61. let priceData = {};
  62. //该用户存储的补充包信息,包括默认展示类型等
  63. let boosterInfo = {};
  64. //缓存的各个游戏对应的卡牌id信息
  65. let cacheInfo = {};
  66. //市场宝珠价格
  67. let gemsSackPrice = 0;
  68. let marketSack = 0;
  69. let customSack = 0;
  70. //是否正在查询卡牌均价
  71. let queryCardPriceFlag = false;
  72. let interval = 8;
  73. //所有游戏的信息,包括appid/name/price/series/available_at_time
  74. let GAME_INFO;
  75. //按钮样式
  76. const classObj = {
  77. disableButton: 'btnv6_blue_blue_innerfade btn_medium btn_makepack btn_disabled',
  78. enableButton: 'btnv6_blue_blue_innerfade btn_medium btn_makepack',
  79. };
  80. const commonButtonStyle = 'height: 26px;width: 44px;margin-right: 8px;';
  81. //游戏操作按钮
  82. const operateButton = {
  83. outToCollect: {
  84. text: '收藏',
  85. style: commonButtonStyle,
  86. desc: '将游戏移入收藏,暂时不做补充包'
  87. },
  88. outToBooster: {
  89. text: '队列',
  90. style: commonButtonStyle + 'background:forestgreen',
  91. desc: '将游戏加入一键做包队列'
  92. },
  93. collectToBooster: {
  94. text: '队列',
  95. style: commonButtonStyle + 'background:forestgreen',
  96. desc: '将游戏加入一键做包队列'
  97. },
  98. collectToOut: {
  99. text: '移出',
  100. style: commonButtonStyle + 'background:darkgoldenrod',
  101. desc: '将游戏从收藏移出'
  102. },
  103. boosterToCollect: {
  104. text: '收藏',
  105. style: commonButtonStyle + 'background:mediumsla teblue',
  106. desc: '将游戏移入收藏,暂时不做补充包'
  107. },
  108. boosterToOut: {
  109. text: '删除',
  110. style: commonButtonStyle + 'background:chocolate',
  111. desc: '将游戏由队列移出到全部'
  112. },
  113. outToBlack: {
  114. text: '拉黑',
  115. style: commonButtonStyle + 'background:black',
  116. desc: '将游戏加入黑名单,暂不考虑做包'
  117. },
  118. blackToOut: {
  119. text: '移出',
  120. style: commonButtonStyle + 'background:darkslategrey',
  121. desc: '将游戏由黑名单移出到全部'
  122. },
  123. reset: {
  124. text: '重置',
  125. style: commonButtonStyle + 'background:maroon',
  126. desc: '将此游戏的缓存信息全部删除,用于解决游戏卡牌能否交易发生变更等问题'
  127. },
  128. };
  129. //卡牌数量对应补充包需要宝石数量
  130. const cardCountToGemsCount = {
  131. 5: 1200,
  132. 6: 1000,
  133. 7: 857,
  134. 8: 750,
  135. 9: 667,
  136. 10: 600,
  137. 11: 545,
  138. 12: 500,
  139. 13: 462,
  140. 14: 429,
  141. 15: 400
  142. }
  143. //消耗宝石数量对应的卡牌数量
  144. const gemsCountToCardCount = {
  145. 1200: 5,
  146. 1000: 6,
  147. 857: 7,
  148. 750: 8,
  149. 667: 9,
  150. 600: 10,
  151. 545: 11,
  152. 500: 12,
  153. 462: 13,
  154. 429: 14,
  155. 400: 15
  156. }
  157.  
  158.  
  159. //url中构建参数
  160. function buildParams(url, data) {
  161. if (data) {
  162. url = url + "?";
  163. for (let [key, value] of Object.entries(data)) {
  164. url = url + key + '=' + value + "&"
  165. }
  166. url = url.substring(0, url.lastIndexOf('&'));
  167. }
  168. return url;
  169. }
  170.  
  171. //构建请求参数
  172. function getRequestData(itemId) {
  173. return {
  174. country: cacheInfo.areaInfo.country,
  175. language: cacheInfo.areaInfo.language,
  176. currency: cacheInfo.areaInfo.currency,
  177. item_nameid: itemId,
  178. two_factor: 0
  179. };
  180. }
  181.  
  182. //获取可通过appid搜索物品信息的游戏列表
  183. function getEnableSearchAppList() {
  184. let url = 'https://steamcommunity.com/market/';
  185. $J.get(url, function (response) {
  186. let parser = new DOMParser();
  187. let searchDoc = parser.parseFromString(response,'text/html');
  188. let searchElement = searchDoc.getElementById('market_advancedsearch_appselect_options_apps');
  189. for(let child of searchElement.children){
  190. enableSearchAppList.push(Number(child.id.split('app_option_')[1]))
  191. }
  192. })
  193. }
  194.  
  195. //获取市场上一袋宝珠价格
  196. function getGemsSackPrice() {
  197. if (marketSack > 0) {
  198. return
  199. }
  200. let sackItemId = cacheInfo.sackItemId;
  201. if (stringNotBlank(sackItemId) && cacheInfo.areaInfo) {
  202. GM_xmlhttpRequest({
  203. method: "GET",
  204. url: buildParams("https://steamcommunity.com/market/itemordershistogram", getRequestData(sackItemId)),
  205. headers: {
  206. "referer": "https://steamcommunity.com/market/listings/753/753",
  207. },
  208. cookie: document.cookie,
  209. onload: function (response) {
  210. console.info("查询一袋宝珠价格")
  211. let price = JSON.parse(response.responseText)
  212. let sellOrder = price.sell_order_graph;
  213. if (sellOrder && sellOrder.length >= 0) {
  214. marketSack = sellOrder[0]['0'];
  215. generateAppInfo(currentAppId);
  216. generateGameList(pageNum, pageSize, searchResult);
  217. }
  218. }
  219. });
  220. } else {
  221. GM_xmlhttpRequest({
  222. method: "GET",
  223. url: "https://steamcommunity.com/market/listings/753/753-Sack%20of%20Gems",
  224. onload: function (response) {
  225. console.info("查询市场上一袋宝珠价格")
  226. let responseData = response.response
  227. let data = {
  228. country: responseData.match(/g_strCountryCode = "([^"]+)"/)[1],
  229. language: responseData.match(/g_strLanguage = "([^"]+)"/)[1],
  230. currency: parseInt(responseData.match(/"wallet_currency":(\d+)/)[1]),
  231. item_nameid: responseData.match(/Market_LoadOrderSpread\( (\d+)/)[1]
  232. };
  233. ///将补充包id放入缓存
  234. cacheInfo.sackItemId = data.item_nameid;
  235. //如果没有区域信息,初始化
  236. if (!cacheInfo.areaInfo || stringBlank(cacheInfo.areaInfo.country)) {
  237. cacheInfo.areaInfo.country = data.country;
  238. cacheInfo.areaInfo.language = data.language;
  239. cacheInfo.areaInfo.currency = data.currency;
  240. }
  241. saveStorage(cacheKey, cacheInfo);
  242. GM_xmlhttpRequest({
  243. method: "GET",
  244. url: buildParams("https://steamcommunity.com/market/itemordershistogram", data),
  245. headers: {
  246. "referer": "https://steamcommunity.com/market/listings/753/753",
  247. },
  248. cookie: document.cookie,
  249. onload: function (response) {
  250. console.info("查询一袋宝珠价格")
  251. let price = JSON.parse(response.responseText)
  252. let sellOrder = price.sell_order_graph;
  253. if (sellOrder && sellOrder.length >= 0) {
  254. marketSack = sellOrder[0]['0'];
  255. generateAppInfo(currentAppId);
  256. generateGameList(pageNum, pageSize, searchResult)
  257. }
  258. }
  259. });
  260. }
  261. });
  262. }
  263. }
  264.  
  265. //构建补充包市场地址
  266. function buildBoosterUrl(item) {
  267. if (!item || stringBlank(item.name)) {
  268. return
  269. }
  270. //特殊处理名字中带 / 、&的游戏
  271. let tempName = item.name.replace(new RegExp('/', 'g'), "-");
  272. let url = 'https://steamcommunity.com/market/listings/753/' + item.appid + '-' + encodeURIComponent(tempName) + '%20Booster%20Pack';
  273. url = url.replace(new RegExp('amp%3B', 'g'), '');
  274. return url
  275. }
  276.  
  277. //查询当前搜索结果的拆包后三张普通卡牌价格
  278. function queryResultCardPrice(i) {
  279. console.info(i)
  280. //判断是否已暂停
  281. if (queryCardPriceFlag) {
  282. if (searchResult.length > 0) {
  283. let item = searchResult[i];
  284. //判断是否查询过,已查询的直接跳转下一个
  285. if (getMarketable(item.appid) && (!priceData[item.appid] || !priceData[item.appid].hadQueryCard)) {
  286. console.info('appid=' + item.appid + ',从缓存查询该游戏卡牌是否可交易:', getMarketable(item.appid))
  287. computeCardPrice(item.appid);
  288. //如果后面还有数据,继续查询
  289. if (i < searchResult.length - 1) {
  290. //每次查询间隔默认8s,防止请求过多被steam封禁
  291. setTimeout(function () {
  292. queryResultCardPrice(i + 1)
  293. }, interval * 1000)
  294. } else {
  295. queryCardPriceFlag = false;
  296. generateGameList(pageNum, pageSize, searchResult)
  297. }
  298. } else {
  299. if (i < searchResult.length - 1) {
  300. queryResultCardPrice(i + 1)
  301. } else {
  302. queryCardPriceFlag = false;
  303. generateGameList(pageNum, pageSize, searchResult)
  304. }
  305. }
  306. }
  307. }
  308. }
  309.  
  310. //查询单个游戏补充包价格
  311. function querySingleBoosterPrice(item) {
  312. //如果已有数据,跳过
  313. if (priceData[item.appid] && priceData[item.appid].hadBooster) {
  314. return
  315. }
  316. let priceInfo = priceData[item.appid] ? priceData[item.appid] : {};
  317. let boosterInfo = cacheInfo.boosterInfo;
  318. //取缓存中的补充包信息
  319. if (boosterInfo && boosterInfo[item.appid]) {
  320. let booster = boosterInfo[item.appid];
  321. $J.ajax({
  322. url: 'https://steamcommunity.com/market/itemordershistogram',
  323. type: 'GET',
  324. data: getRequestData(booster.itemId)
  325. }).success(function (price) {
  326. console.info("查询补充包价格:", item.appid)
  327. let buyOrder = price.buy_order_graph;
  328. priceInfo.hadBooster = true;
  329. if (buyOrder && buyOrder.length >= 0) {
  330. priceInfo.buyPrice = buyOrder[0]['0']
  331. } else {
  332. priceInfo.buyPrice = '未知';
  333. }
  334. generateCreateButton();
  335. generateGameList(pageNum, pageSize, searchResult)
  336. }).error(function () {
  337. generateCreateButton();
  338. generateGameList(pageNum, pageSize, searchResult)
  339. })
  340. } else {
  341. //缓存中如果没有补充包id,获取并放入缓存
  342. console.info('查询补充包',item)
  343. $J.get(buildBoosterUrl(item), function (data) {
  344. let itemId = data.match(/Market_LoadOrderSpread\( (\d+)/)[1];
  345. let currency = parseInt(data.match(/"wallet_currency":(\d+)/)[1]);
  346. let language = data.match(/g_strLanguage = "([^"]+)"/)[1];
  347. let country = data.match(/g_strCountryCode = "([^"]+)"/)[1];
  348. $J.ajax({
  349. url: 'https://steamcommunity.com/market/itemordershistogram',
  350. type: 'GET',
  351. data: {
  352. country: country,
  353. language: language,
  354. currency: currency,
  355. item_nameid: itemId
  356. }
  357. }).success(function (price) {
  358. console.info("查询补充包价格:", item.appid)
  359. let buyOrder = price.buy_order_graph;
  360. priceInfo.hadBooster = true;
  361. if (buyOrder && buyOrder.length >= 0) {
  362. priceInfo.buyPrice = buyOrder[0]['0']
  363. } else {
  364. priceInfo.buyPrice = '未知';
  365. }
  366. //构建补充包缓存信息,存入补充包id
  367. let booster = {};
  368. booster.itemId = itemId;
  369. //如果时第一次存放补充包信息,初始化
  370. if (!cacheInfo.boosterInfo) {
  371. cacheInfo.boosterInfo = {}
  372. }
  373. //如果没有区域信息,初始化
  374. if (!cacheInfo.areaInfo) {
  375. cacheInfo.areaInfo.country = country;
  376. cacheInfo.areaInfo.language = language;
  377. cacheInfo.areaInfo.currency = currency;
  378. }
  379. cacheInfo.boosterInfo[item.appid] = booster;
  380. saveStorage(cacheKey, cacheInfo);
  381.  
  382. generateCreateButton();
  383. generateGameList(pageNum, pageSize, searchResult)
  384. }).error(function () {
  385. generateCreateButton();
  386. generateGameList(pageNum, pageSize, searchResult)
  387. });
  388. });
  389. }
  390. priceData[item.appid] = priceInfo
  391. //查询销量
  392. $J.ajax({
  393. url: 'https://steamcommunity.com/market/priceoverview',
  394. type: 'GET',
  395. data: {
  396. country: cacheInfo.areaInfo.country,
  397. currency: cacheInfo.areaInfo.currency,
  398. appid: 753,
  399. market_hash_name: item.appid + '-' + item.name + ' Booster Pack'
  400. }
  401. }).success(function (data) {
  402. if (data && data.volume) {
  403. priceInfo.sold = data.volume
  404. } else {
  405. priceInfo.sold = 0
  406. }
  407. generateCreateButton()
  408. generateGameList(pageNum, pageSize, searchResult)
  409. }).error(function () {
  410. generateCreateButton()
  411. generateGameList(pageNum, pageSize, searchResult)
  412. });
  413. }
  414.  
  415.  
  416. //查询当前游戏的卡牌是否可以交易,修改为异步
  417. function queryMarketable(appid) {
  418. console.info("查询是否可交易,appid:", appid)
  419. let marketable = false;
  420. let url = 'https://steamcommunity.com/market/search/render/?start=0&count=20&category_753_cardborder[]=tag_cardborder_0&appid=753&category_753_Game[]=tag_app_' + appid;
  421. $J.get(url, function (response) {
  422. //卡牌总数
  423. let cardCount = response.total_count;
  424. if (!cardCount) {
  425. return;
  426. }
  427. let cardList = $J('<div>' + response.results_html + '</div>');
  428. let cardUrl = cardList.find('.market_listing_row_link')[0].href;
  429. $J.get(cardUrl, function (data) {
  430. marketable = parseInt(data.match(/"marketable":(\d+)/)[1]) === 1;
  431. let cardCacheItem = cacheInfo.cardInfo[appid] ? cacheInfo.cardInfo[appid] : {};
  432. cardCacheItem.marketable = marketable;
  433. cacheInfo.cardInfo[appid] = cardCacheItem;
  434. saveStorage(cacheKey, cacheInfo);
  435. generateGameList(pageNum, pageSize, searchResult);
  436. })
  437. })
  438. return marketable;
  439. }
  440.  
  441. //根据缓存判断某个游戏卡牌是否可交易
  442. function getMarketable(appid) {
  443. //非补充包页面,默认可交易,查询市场
  444. if (pageLocation !== 2) {
  445. return true;
  446. }
  447. let cardInfo = cacheInfo.cardInfo;
  448. let cacheItem = cardInfo ? cardInfo[appid] : {};
  449. //不可交易,直接返回
  450. return !(cacheItem && cacheItem.marketable !== undefined && !cacheItem.marketable);
  451. }
  452.  
  453. //根据appid估算拆包后三张卡牌平均总价,按照最低售价计算
  454. function computeCardPrice(appid) {
  455. let priceInfo = priceData[appid] ? priceData[appid] : {};
  456. //防重复
  457. if (priceInfo.hadQueryCard) {
  458. generateAppInfo(currentAppId)
  459. return
  460. }
  461. priceData[appid] = priceInfo;
  462.  
  463. let cardInfo = cacheInfo.cardInfo;
  464. let cacheItem = cardInfo ? cardInfo[appid] : {};
  465. //不可交易,直接返回
  466. if (!getMarketable(appid)) {
  467. priceData[appid].hadQueryCard = true;
  468. return
  469. }
  470. if (cacheItem && cacheItem.cardIdList) {
  471. let count = 0;
  472. let successCount = 0;
  473. let totalPrice = 0;
  474. let totalSellerCount = 0;
  475. let exceptionInfo = '';
  476. cacheItem.cardIdList.forEach(id => {
  477. GM_xmlhttpRequest({
  478. method: "GET",
  479. url: buildParams("https://steamcommunity.com/market/itemordershistogram", getRequestData(id)),
  480. headers: {
  481. // "host":"steamcommunity.com",
  482. "referer": "https://steamcommunity.com/market/listings/753/" + appid,
  483. // "user-agent":navigator.userAgent,
  484. },
  485. synchronous: false,
  486. onload: function (response) {
  487. let price = JSON.parse(response.responseText)
  488. count++;
  489. let minSellPrice = price.lowest_sell_order;
  490. let tempDiv = document.createElement('div');
  491. tempDiv.innerHTML = price.sell_order_summary;
  492. let sellerCountString = tempDiv.querySelector('.market_commodity_orders_header_promote').textContent;
  493. if (minSellPrice === undefined || minSellPrice.length === 0) {
  494. exceptionInfo = 'Query order afresh,sellOrder is undefined,appid:' + appid + ",cardId:" + id;
  495. }else {
  496. totalPrice += parseInt(minSellPrice)/100;
  497. successCount++;
  498. }
  499. if (sellerCountString !== undefined && sellerCountString.length > 0){
  500. totalSellerCount += parseInt(sellerCountString,10)
  501. }
  502. if (count === cacheItem.count) {
  503. if (cacheItem.marketable === undefined) {
  504. queryMarketable(appid);
  505. }
  506. if (successCount < count) {
  507. console.error("卡牌总量:" + count + ",查询成功数量:" + successCount)
  508. queryMarketable(appid);
  509. }
  510. if (successCount === 0 && stringNotBlank(exceptionInfo)) {
  511. console.info(exceptionInfo, " appid = ", appid);
  512. cacheInfo.cardInfo[appid] = undefined;
  513. saveStorage(cacheKey, cacheInfo);
  514. computeCardPrice(appid);
  515. return;
  516. }
  517. priceInfo.cardPrice = 3 * (totalPrice / successCount);
  518. priceInfo.aveargeSellerCount = totalSellerCount / count;
  519. priceData[appid] = priceInfo;
  520. priceData[appid].hadQueryCard = true;
  521. generateAppInfo(currentAppId);
  522. generateGameList(pageNum, pageSize, searchResult);
  523. }
  524. }
  525. });
  526. })
  527. } else {
  528. if (queryCard) {
  529. return;
  530. }
  531. queryCard = true;
  532.  
  533. //查询卡牌是否可交易
  534. cacheItem = {};
  535. // cacheItem.marketable = queryMarketable(appid);
  536. cacheInfo.cardInfo[appid] = cacheItem;
  537. saveStorage(cacheKey, cacheInfo);
  538. if (!getMarketable(appid)) {
  539. priceData[appid].hadQueryCard = true;
  540. generateGameList(pageNum, pageSize, searchResult);
  541. queryCard = false;
  542. return
  543. }
  544. //获取所有卡牌
  545. let url = 'https://steamcommunity.com/market/search/render/?start=0&count=20&category_753_cardborder[]=tag_cardborder_0&appid=753&category_753_Game[]=tag_app_' + appid;
  546. GM_xmlhttpRequest({
  547. method: "GET",
  548. url: url,
  549. headers: {
  550. "referer": "https://steamcommunity.com/market/listings/753/" + appid,
  551. },
  552. onload: function (response) {
  553. console.info("查询所有卡牌,appid:", appid);
  554. let data = JSON.parse(response.response)
  555. //卡牌总数
  556. let cardCount = data.total_count
  557. if (!cardCount) {
  558. queryCard = false;
  559. return
  560. }
  561. //构建缓存信息
  562. let cardCacheItem = cacheInfo.cardInfo[appid] ? cacheInfo.cardInfo[appid] : {};
  563. if (cacheItem && cacheItem.madeCount) {
  564. cardCacheItem.madeCount = cacheItem.madeCount;
  565. }
  566. cardCacheItem.cardIdList = []
  567. let count = 0;
  568. let successCount = 0;
  569. let totalPrice = 0;
  570. let cardList = $J('<div>' + data.results_html + '</div>');
  571. cardList.find('.market_listing_row_link').each(function () {
  572. let item = $J(this);
  573. let link = item.attr('href');
  574. GM_xmlhttpRequest({
  575. method: "GET",
  576. url: link,
  577. headers: {
  578. "referer": "https://steamcommunity.com/market/listings/753/" + appid,
  579. },
  580. onload: function (pageData) {
  581. let responseData = pageData.response
  582. let id = responseData.match(/Market_LoadOrderSpread\( (\d+)/)[1];
  583. let data = {
  584. country: responseData.match(/g_strCountryCode = "([^"]+)"/)[1],
  585. language: responseData.match(/g_strLanguage = "([^"]+)"/)[1],
  586. currency: parseInt(responseData.match(/"wallet_currency":(\d+)/)[1]),
  587. item_nameid: id
  588. };
  589. let cardUrl = "https://steamcommunity.com/market/itemordershistogram";
  590. GM_xmlhttpRequest({
  591. method: "GET",
  592. url: buildParams(cardUrl, data),
  593. headers: {
  594. "referer": "https://steamcommunity.com/market/listings/753/" + appid,
  595. },
  596. cookie: document.cookie,
  597. onload: function (response) {
  598. let price = JSON.parse(response.responseText)
  599. count++;
  600. let sellOrder = price.sell_order_graph;
  601. //保存游戏名称
  602. cardCacheItem.name = pageData.response.toString().match(/sting_game_name">([^"]+) 集换式卡牌/)[1]
  603. console.info("查询卡牌价格,appName:", cardCacheItem.name, "appid:", appid, ",cardId:", id, ",index:", count)
  604. if (sellOrder && sellOrder.length >= 0 && sellOrder[0]) {
  605. totalPrice += sellOrder[0]['0'];
  606. successCount++;
  607. }
  608. //在缓存中添加卡牌id
  609. cardCacheItem.cardIdList.push(data.item_nameid);
  610. if (cardCount === count) {
  611. if (cacheItem.marketable === undefined) {
  612. queryMarketable(appid);
  613. }
  614. priceInfo.cardPrice = 3 * (totalPrice / successCount);
  615. if (successCount < cardCount) {
  616. console.error("卡牌总量:" + cardCount + ",查询成功数量:" + successCount)
  617. }
  618. priceData[appid] = priceInfo;
  619.  
  620.  
  621. currentAppName = cardCacheItem.name
  622. //缓存卡牌id信息等
  623. cacheInfo.areaInfo.country = data.country;
  624. cacheInfo.areaInfo.language = data.language;
  625. cacheInfo.areaInfo.currency = data.currency;
  626.  
  627. cardCacheItem.count = cardCount;
  628.  
  629. cacheInfo.cardInfo[appid] = cardCacheItem;
  630. saveStorage(cacheKey, cacheInfo);
  631. queryCard = false;
  632. priceData[appid].hadQueryCard = true;
  633. generateAppInfo(currentAppId)
  634. generateGameList(pageNum, pageSize, searchResult)
  635. }
  636. }
  637. });
  638. }
  639. });
  640. });
  641. }
  642. });
  643. }
  644. if (boosterInfo.autoQueryBundle !== undefined && boosterInfo.autoQueryBundle){
  645. querySingleBoosterPrice(GAME_INFO[appid])
  646. }
  647. }
  648.  
  649. //制作单个补充包
  650. function createSingleBooster(item) {
  651. if (!item || !item.appid) {
  652. return
  653. }
  654. $J.ajax({
  655. url: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/',
  656. type: 'POST',
  657. data: {
  658. sessionid: sessionId,
  659. appid: item.appid,
  660. series: GAME_INFO[item.appid].series,
  661. tradability_preference: 1
  662. },
  663. crossDomain: true,
  664. xhrFields: {withCredentials: true}
  665. }).success(function () {
  666. console.info("制作补充包:", item.appid)
  667. item.available_at_time = '已完成';
  668. doneList.push(item.appid.toString());
  669.  
  670. //历史制作数量+1
  671. let cardInfo = cacheInfo.cardInfo;
  672. let cacheItem = cardInfo[item.appid];
  673. if (cacheItem) {
  674. if (isNaN(cacheItem.madeCount)) {
  675. cacheItem.madeCount = 1;
  676. } else {
  677. cacheItem.madeCount = cacheItem.madeCount + 1;
  678. }
  679. } else {
  680. cacheItem = {};
  681. cacheItem.madeCount = 1;
  682. }
  683. cacheItem.lastMade = getCurrentDay()
  684. cardInfo[item.appid] = cacheItem;
  685. saveStorage(cacheKey, cacheInfo);
  686.  
  687. setUnavailable();
  688. buildOptions();
  689. generateCreateButton();
  690. generateGameList(pageNum, pageSize, searchResult);
  691. }).error(function () {
  692. item.available_at_time = '制作失败';
  693. document.getElementById("createButton").innerHTML = "宝石不足或其他原因";
  694. generateGameList(pageNum, pageSize, searchResult);
  695. });
  696. }
  697.  
  698. //获取当前时间,格式:2021-01-01
  699. function getCurrentDay() {
  700. const now = new Date();
  701. const year = now.getFullYear();
  702. const month = (now.getMonth() + 1).toString().padStart(2, '0');
  703. const date = now.getDate().toString().padStart(2, '0');
  704. return `${year}-${month}-${date}`;
  705. }
  706.  
  707. //获取当前时间,格式:2021-01-01 12:00:00
  708. function getCurrentTime() {
  709. const now = new Date();
  710. const year = now.getFullYear();
  711. const month = (now.getMonth() + 1).toString().padStart(2, '0');
  712. const date = now.getDate().toString().padStart(2, '0');
  713. const hours = now.getHours().toString().padStart(2, '0');
  714. const minutes = now.getMinutes().toString().padStart(2, '0');
  715. const seconds = now.getSeconds().toString().padStart(2, '0');
  716. return `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`;
  717. }
  718.  
  719. //循环制作补充包
  720. function createBooster(index) {
  721. if (availableGame.length === 0) {
  722. return
  723. }
  724. if (index === 0) {
  725. //重复点击直接返回
  726. if (document.getElementById("createButton").innerHTML === '正在制作') {
  727. return
  728. } else {
  729. document.getElementById("createButton").innerHTML = "正在制作"
  730. }
  731. }
  732. let item = availableGame[index];
  733.  
  734. //不可交易的包,跳过
  735. if (!getMarketable(item.value)) {
  736. console.info("卡牌不可交易,跳过 appid = ", item.value);
  737. if (index + 1 < availableGame.length) {
  738. createBooster(index + 1)
  739. } else {
  740. setUnavailable();
  741. buildOptions();
  742. generateCreateButton();
  743. generateGameList(pageNum, pageSize, searchResult);
  744. }
  745. } else {
  746. $J.ajax({
  747. url: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/',
  748. type: 'POST',
  749. data: {
  750. sessionid: sessionId,
  751. appid: item.value,
  752. series: GAME_INFO[item.value].series,
  753. tradability_preference: 1
  754. },
  755. crossDomain: true,
  756. xhrFields: {withCredentials: true}
  757. }).success(function () {
  758. console.info("制作补充包成功 appid = ", item.value);
  759. doneList.push(item.value);
  760.  
  761. //将对应补充包置为 已完成
  762. for (let searchItem of searchResult) {
  763. if (searchItem && searchItem.appid.toString() === item.value.toString()) {
  764. searchItem.available_at_time = '已完成';
  765. }
  766. }
  767.  
  768. //历史制作数量+1
  769. let cardInfo = cacheInfo.cardInfo;
  770. let cacheItem = cardInfo[item.value];
  771. if (cacheItem) {
  772. if (isNaN(cacheItem.madeCount)) {
  773. cacheItem.madeCount = 1;
  774. } else {
  775. cacheItem.madeCount = cacheItem.madeCount + 1;
  776. }
  777. } else {
  778. cacheItem = {};
  779. cacheItem.madeCount = 1;
  780. }
  781. cardInfo[item.value] = cacheItem;
  782. saveStorage(cacheKey, cacheInfo);
  783.  
  784. if (index + 1 < availableGame.length) {
  785. createBooster(index + 1)
  786. } else {
  787. setUnavailable();
  788. buildOptions();
  789. generateCreateButton();
  790. generateGameList(pageNum, pageSize, searchResult);
  791. }
  792. }).error(function () {
  793. document.getElementById("createButton").innerHTML = "宝石不足或其他原因"
  794. });
  795. }
  796. }
  797.  
  798. //制包成功后将对应option设置为unavailable
  799. function setUnavailable() {
  800. if (doneList && doneList.length > 0) {
  801. for (let i = 0; i < backUpOptions.length; i++) {
  802. if (doneList.indexOf(backUpOptions[i].value) > -1) {
  803. backUpOptions[i].setAttribute("class", "unavailable")
  804. }
  805. }
  806. }
  807. }
  808.  
  809. //判断字符串是否为空,不为空是true,为空是false
  810. function stringNotBlank(value) {
  811. return !stringBlank(value);
  812. }
  813.  
  814. //判断字符串是否为空,为空是true,不为空是false
  815. function stringBlank(value) {
  816. return !value || value.trim() === '';
  817. }
  818.  
  819. //将所有未收藏游戏移入黑名单
  820. function toBlack() {
  821. if (!outOptions || outOptions.length === 0) {
  822. return
  823. }
  824. outOptions.map(function (item) {
  825. if (boosterInfo.black.indexOf(item.value) === -1) {
  826. operateGame(item.value, "outToBlack")
  827. }
  828. })
  829. }
  830.  
  831. //执行搜索
  832. function doSearch() {
  833. //只要执行搜索,首先暂停查询
  834. queryCardPriceFlag = false;
  835.  
  836. let inputValue = document.getElementById('searchInput').value;
  837. let typeSelect = document.getElementById('typeSelect');
  838. let pageSizeValue = document.getElementById('pageSizeInput').value;
  839. let sourceSelect = document.getElementById('sourceSelect');
  840.  
  841.  
  842. if (pageSizeValue && !isNaN(pageSizeValue) && /(^[1-9]\d*$)/.test(pageSizeValue) && pageSizeValue !== boosterInfo.pageSize) {
  843. pageSize = parseInt(pageSizeValue);
  844. boosterInfo.pageSize = pageSize;
  845. saveStorage(boosterKey, boosterInfo)
  846. }
  847.  
  848. if (stringNotBlank(inputValue)) {
  849. searchInfo = inputValue.trim()
  850. } else {
  851. searchInfo = ''
  852. }
  853. if (boosterInfo.typeIndex !== typeSelect.selectedIndex) {
  854. boosterInfo.typeIndex = typeSelect.selectedIndex;
  855. saveStorage(boosterKey, boosterInfo)
  856. }
  857.  
  858. //如果是自定义价格,保存
  859. if (sourceSelect.selectedIndex === 1) {
  860. let gemPriceInputValue = document.getElementById('gemPriceInput').value;
  861. if (gemPriceInputValue && !isNaN(gemPriceInputValue)) {
  862. boosterInfo.sourceIndex = 1;
  863. boosterInfo.customPrice = gemPriceInputValue;
  864. saveStorage(boosterKey, boosterInfo)
  865. customSack = gemPriceInputValue
  866. }
  867. } else {
  868. boosterInfo.sourceIndex = 0;
  869. saveStorage(boosterKey, boosterInfo)
  870. }
  871.  
  872. saveCustomizeInterval();
  873.  
  874. searchResult = [];
  875. let tempGameInfo = {};
  876.  
  877. if (boosterInfo.typeIndex === 0) {
  878. backUpOptions.map(function (item) {
  879. tempGameInfo[item.value] = GAME_INFO[item.value]
  880. })
  881. } else if (boosterInfo.typeIndex === 1) {
  882. boosterOptions.map(function (item) {
  883. tempGameInfo[item.value] = GAME_INFO[item.value]
  884. })
  885. } else if (boosterInfo.typeIndex === 2) {
  886. collectOptions.map(function (item) {
  887. tempGameInfo[item.value] = GAME_INFO[item.value]
  888. })
  889. } else if (boosterInfo.typeIndex === 3) {
  890. outOptions.map(function (item) {
  891. tempGameInfo[item.value] = GAME_INFO[item.value]
  892. })
  893. } else if (boosterInfo.typeIndex === 4) {
  894. blackOptions.map(function (item) {
  895. tempGameInfo[item.value] = GAME_INFO[item.value]
  896. })
  897. }
  898.  
  899. if (stringBlank(searchInfo)) {
  900. for (let key in tempGameInfo) {
  901. searchResult.push(tempGameInfo[key])
  902. }
  903. } else {
  904. //支持大小写不敏感匹配、appid匹配、多个字符串匹配
  905. for (let key in tempGameInfo) {
  906. if (searchInfo.indexOf(' ') !== -1) {
  907. let match = true;
  908. let keyWordArray = searchInfo.split(' ');
  909. for (let i = 0; i < keyWordArray.length; i++) {
  910. if (tempGameInfo[key].name.toUpperCase().indexOf(keyWordArray[i].trim().toUpperCase()) === -1) {
  911. match = false;
  912. break
  913. }
  914. }
  915. if (match) {
  916. searchResult.push(tempGameInfo[key])
  917. }
  918. } else if (searchInfo === tempGameInfo[key].appid.toString() || tempGameInfo[key].name.toUpperCase().indexOf(searchInfo.toUpperCase()) > -1) {
  919. searchResult.push(tempGameInfo[key])
  920. }
  921. }
  922. }
  923. pageNum = 1;
  924. generateGameList(pageNum, pageSize, searchResult)
  925. }
  926.  
  927. //构建市场搜索链接
  928. function buildMarketUrl(name, appid) {
  929. if (enableSearchAppList.indexOf(appid) > -1){
  930. return 'https://steamcommunity.com/market/search?appid=' + appid
  931. }else {
  932. if (stringBlank(name)) {
  933. return 'https://store.steampowered.com/'
  934. } else {
  935. let searchUrl = 'https://steamcommunity.com/market/search?q=';
  936. let keyArray = name.split(' ');
  937. searchUrl += keyArray[0];
  938. for (let i = 1; i < keyArray.length; i++) {
  939. searchUrl = searchUrl + '+' + keyArray[i]
  940. }
  941. searchUrl = searchUrl.replace(new RegExp('&amp;', 'g'), '%26');
  942. return searchUrl
  943. }
  944. }
  945. }
  946.  
  947. //保存自定义查询卡牌价格时间间隔
  948. function saveCustomizeInterval() {
  949. let intervalValue = document.getElementById('timeInterval').value;
  950. if (intervalValue && !isNaN(intervalValue) && intervalValue >= 1 && intervalValue !== interval) {
  951. boosterInfo.timeInterval = intervalValue;
  952. saveStorage(boosterKey, boosterInfo);
  953. interval = intervalValue
  954. }
  955. }
  956.  
  957. //查询当前游戏多少张卡牌
  958. function getCardCount(currentAppId) {
  959. if (!currentAppId) {
  960. return 0;
  961. }
  962. //如果已有缓存,直接返回
  963. if (cacheInfo && cacheInfo.cardInfo && cacheInfo.cardInfo[currentAppId]) {
  964. return cacheInfo.cardInfo[currentAppId].count;
  965. }
  966. let url = 'https://steamcommunity.com/market/search/render/?start=0&count=20&category_753_cardborder[]=tag_cardborder_0&appid=753&category_753_Game[]=tag_app_' + currentAppId;
  967. GM_xmlhttpRequest({
  968. method: "GET",
  969. url: url,
  970. headers: {
  971. "referer": "https://steamcommunity.com/market/listings/753/" + currentAppId,
  972. },
  973. onload: function (response) {
  974. let data = JSON.parse(response.response)
  975. let cardCount = data.total_count
  976. if (!cardCount) {
  977. return
  978. }
  979. //构建缓存信息
  980. let cardCacheItem = {};
  981. cardCacheItem.count = cardCount;
  982. cacheInfo.cardInfo[currentAppId] = cardCacheItem;
  983. saveStorage(cacheKey, cacheInfo);
  984. return cardCount;
  985. }
  986. })
  987. }
  988.  
  989. //计算当前游戏补充包成本
  990. function getBoosterCost(currentAppId) {
  991. if (marketSack <= 0) {
  992. return "宝珠成本未知"
  993. }
  994. let cardCount = getCardCount(currentAppId);
  995. if (cardCount <= 0) {
  996. return "卡牌数量未知"
  997. }
  998. let gemsCount = cardCountToGemsCount[cardCount];
  999. if (gemsCount === undefined) {
  1000. return "未知游戏类型"
  1001. }
  1002. let cost = parseInt(gemsCount) / 1000 * marketSack;
  1003. if (!isNaN(cost)) {
  1004. return parseFloat(cost.toFixed(2));
  1005. } else {
  1006. return "计算错误"
  1007. }
  1008. }
  1009.  
  1010. //渲染市场搜索页面,用红色标注卡牌
  1011. function generateSearchMarket() {
  1012. let result = document.getElementsByClassName("market_listing_game_name");
  1013. if (pageLocation === 5 && result !== null && result.length !== 0) {
  1014. for (let i = 0; i < result.length; i++) {
  1015. let item = result[i]
  1016. if (item.innerText.indexOf('集换式卡牌') !== -1 && item.parentNode.parentNode.className === 'market_listing_row market_recent_listing_row market_listing_searchresult') {
  1017. item.parentNode.parentNode.parentNode.setAttribute('target', '_blank');
  1018. if (item.innerText.indexOf('闪亮') !== -1) {
  1019. item.setAttribute('style', 'color:yellow;');
  1020. item.parentNode.parentNode.getElementsByClassName("market_listing_num_listings_qty")[0].setAttribute('style', 'color:yellow;');
  1021. } else {
  1022. item.setAttribute('style', 'color:red;');
  1023. item.parentNode.parentNode.getElementsByClassName("market_listing_num_listings_qty")[0].setAttribute('style', 'color:red;');
  1024. }
  1025. }
  1026. }
  1027. }
  1028. setTimeout(function () {
  1029. generateSearchMarket()
  1030. }, 300)
  1031. }
  1032.  
  1033.  
  1034. //渲染游戏详情页补充包信息
  1035. function generateAppInfo(currentAppId) {
  1036. if (pageLocation === 4) {
  1037. gameDes = document.getElementById("error_box")
  1038. }
  1039. if (!gameDes) {
  1040. return;
  1041. }
  1042. if (drawing) {
  1043. return;
  1044. }
  1045. drawing = true;
  1046. if (document.getElementById("boosterInfo")) {
  1047. gameDes.removeChild(document.getElementById("boosterInfo"))
  1048. }
  1049.  
  1050. let boosterInfo = document.createElement('div');
  1051. boosterInfo.setAttribute('id', 'boosterInfo');
  1052. boosterInfo.setAttribute('style', 'height:40px;width:100%;margin-bottom:12px');
  1053.  
  1054. let marketPriceInfo = document.createElement('span');
  1055. marketPriceInfo.innerHTML = '一袋宝珠:' + marketSack;
  1056. marketPriceInfo.setAttribute('title', '当前市场一袋补充包最低售价');
  1057.  
  1058. let cardCount = document.createElement('span');
  1059. cardCount.innerHTML = '此游戏卡牌:' + getCardCount(currentAppId);
  1060. cardCount.setAttribute('title', '当前游戏一套卡牌数量');
  1061. cardCount.setAttribute('style', 'display: inline-block; margin-left: 8px');
  1062.  
  1063. let costInfo = document.createElement('span');
  1064. let cost = getBoosterCost(currentAppId);
  1065. costInfo.innerHTML = '成本:' + cost;
  1066. costInfo.setAttribute('title', '按照市场宝珠价格计算制作此游戏一个补充包成本');
  1067. costInfo.setAttribute('style', 'display: inline-block; margin-left: 8px');
  1068.  
  1069.  
  1070. //计算当前补充包的三张卡牌均价
  1071. if (!priceData[currentAppId] || !priceData[currentAppId].cardPrice) {
  1072. computeCardPrice(currentAppId);
  1073. }
  1074. let cardPrice = document.createElement('span');
  1075. let tempCardPrice = priceData[currentAppId].cardPrice;
  1076. if (!isNaN(tempCardPrice)) {
  1077. tempCardPrice = (tempCardPrice / 1.15).toFixed(2);
  1078. cardPrice.innerHTML = '售价:' + tempCardPrice;
  1079. if (cost > 0 && tempCardPrice > cost) {
  1080. cardPrice.setAttribute('style', 'display: inline-block; margin-left: 8px;color:red');
  1081. } else {
  1082. cardPrice.setAttribute('style', 'display: inline-block; margin-left: 8px');
  1083. }
  1084. }
  1085. cardPrice.setAttribute('title', '拆包后三张卡牌均价总和,即一个补充包平均拆包后卖出价格(税后)');
  1086.  
  1087. let marketInfo = document.createElement('a');
  1088. marketInfo.innerHTML = '游戏物品';
  1089. marketInfo.setAttribute('href', buildMarketUrl(currentAppName,currentAppId));
  1090. marketInfo.setAttribute('style', 'display: inline-block; margin-left: 8px');
  1091. marketInfo.setAttribute('target', '_blank');
  1092.  
  1093. let boosterUrl = document.createElement('a');
  1094. boosterUrl.innerHTML = '做包';
  1095. boosterUrl.setAttribute('href', 'https://steamcommunity.com//tradingcards/boostercreator/');
  1096. boosterUrl.setAttribute('style', 'display: inline-block; margin-left: 8px');
  1097. boosterUrl.setAttribute('target', '_blank');
  1098.  
  1099. let marketUrl = document.createElement('a');
  1100. marketUrl.innerHTML = '解决软锁';
  1101. marketUrl.setAttribute('title', '进入单独查看价格页面,可将软锁游戏加入购物车');
  1102. marketUrl.setAttribute('href', 'https://store.steampowered.com/widget/' + currentAppId);
  1103. marketUrl.setAttribute('style', 'display: inline-block; margin-left: 8px');
  1104. marketUrl.setAttribute('target', '_blank');
  1105.  
  1106. boosterInfo.appendChild(marketPriceInfo);
  1107. boosterInfo.appendChild(cardCount);
  1108. boosterInfo.appendChild(costInfo);
  1109. boosterInfo.appendChild(cardPrice);
  1110. boosterInfo.appendChild(marketInfo);
  1111. boosterInfo.appendChild(boosterUrl);
  1112. boosterInfo.appendChild(marketUrl);
  1113.  
  1114. gameDes.insertBefore(boosterInfo, gameDes.firstChild);
  1115. drawing = false;
  1116. }
  1117.  
  1118.  
  1119. //生成游戏列表
  1120. function generateGameList(pageNum, pageSize, searchResult) {
  1121. //不是补充包页面,返回
  1122. if (!gameForm) {
  1123. return;
  1124. }
  1125. //重新生成,删除旧数据
  1126. for (let i = gameForm.childNodes.length - 1; i >= 0; i--) {
  1127. gameForm.removeChild(gameForm.childNodes[i]);
  1128. }
  1129. gameForm.setAttribute('style', 'width:860px;');
  1130.  
  1131. //补充包成本展示
  1132. let gemsDiv = document.createElement('div');
  1133. gemsDiv.setAttribute('style', 'width:100%;margin-bottom:12px');
  1134.  
  1135. //计算当前展示页的起始位置
  1136. let startIndex = (pageNum - 1) * pageSize;
  1137.  
  1138. //选择成本提示
  1139. let boosterCost = document.createElement('span');
  1140. boosterCost.setAttribute('title', '选择你一袋宝珠成本,用于计算制作补充包是否可以获利');
  1141. boosterCost.innerHTML = '一袋宝珠成本: ';
  1142.  
  1143. //下拉选择使用市场价还是自定义价格
  1144. let sourceSelect = document.createElement('select');
  1145. sourceSelect.setAttribute('style', 'margin-right:22px;width:130px');
  1146. sourceSelect.setAttribute('id', 'sourceSelect');
  1147.  
  1148. let customOption = document.createElement('option');
  1149. customOption.setAttribute('value', '2');
  1150. customOption.innerHTML = '自定义价格';
  1151.  
  1152. let marketOption = document.createElement('option');
  1153. marketOption.setAttribute('value', '1');
  1154. marketOption.innerHTML = '市场价格';
  1155.  
  1156. sourceSelect.add(marketOption);
  1157. sourceSelect.add(customOption);
  1158.  
  1159. sourceSelect.selectedIndex = boosterInfo.sourceIndex;
  1160. sourceSelect.onchange = function () {
  1161. doSearch();
  1162. };
  1163.  
  1164. let customPriceInfo = document.createElement('span');
  1165. customPriceInfo.innerHTML = '自定义成本: ';
  1166. customPriceInfo.setAttribute('style', 'margin-left: 12px;')
  1167. customPriceInfo.setAttribute('title', '你自己的一袋补充包购买成本');
  1168.  
  1169.  
  1170. //自定义价格输入框
  1171. let gemPriceInput = document.createElement('input');
  1172. gemPriceInput.setAttribute('id', 'gemPriceInput');
  1173. gemPriceInput.setAttribute('title', '只有宝珠成本选择‘自定义价格’时输入才会生效');
  1174. gemPriceInput.setAttribute('style', 'background-color: rgba( 103, 193, 245, 0.2 ); color: #fff; border: 1px solid #000;border-radius: 3px; width: 30px;padding: 5px;');
  1175. gemPriceInput.value = boosterInfo.customPrice;
  1176. gemPriceInput.onchange = function () {
  1177. doSearch()
  1178. };
  1179.  
  1180. let marketPriceInfo = document.createElement('a');
  1181. marketPriceInfo.innerHTML = '市场价格:' + marketSack;
  1182. marketPriceInfo.setAttribute('title', '当前市场一袋宝珠最低售价,点击进入宝珠市场页面');
  1183. marketPriceInfo.setAttribute('target', '_blank');
  1184. marketPriceInfo.setAttribute('href', 'https://steamcommunity.com/market/listings/753/753-Sack%20of%20Gems');
  1185. marketPriceInfo.setAttribute('style', 'display: inline-block;text-decoration:underline;cursor: pointer;color:#8F98A0')
  1186.  
  1187.  
  1188. let blackButton = document.createElement('button');
  1189. blackButton.innerHTML = '一键拉黑';
  1190. blackButton.setAttribute('class', outOptions.length > 0 ? classObj.enableButton : classObj.disableButton);
  1191. blackButton.setAttribute('title', '将所有未收藏游戏移入黑名单');
  1192. blackButton.setAttribute('style', 'margin-left: 15px;width: 90px; height: 26px;');
  1193. blackButton.onclick = function () {
  1194. toBlack()
  1195. };
  1196. //查询当前搜索结果卡牌均价
  1197. let autoQueryPrice = document.createElement('button');
  1198. autoQueryPrice.innerHTML = queryCardPriceFlag ? '暂停查询' : '自动查询';
  1199. autoQueryPrice.setAttribute('class', classObj.enableButton);
  1200. autoQueryPrice.setAttribute('style', 'width: 90px; height: 26px;float: right');
  1201. autoQueryPrice.setAttribute('title', queryCardPriceFlag ? "点击将暂停自动查询" : "点击从当前页开始自动查询当前所有搜索结果的三张卡牌均价,已查询或不可交易卡牌会跳过,请勿重复快速点击");
  1202. if (queryCardPriceFlag) {
  1203. autoQueryPrice.onclick = function () {
  1204. queryCardPriceFlag = false;
  1205. saveCustomizeInterval();
  1206. generateGameList(pageNum, pageSize, searchResult);
  1207. };
  1208. } else {
  1209. autoQueryPrice.onclick = function () {
  1210. queryCardPriceFlag = true;
  1211. saveCustomizeInterval();
  1212. generateGameList(pageNum, pageSize, searchResult);
  1213. queryResultCardPrice(startIndex);
  1214. };
  1215. }
  1216. //自动查询补充包
  1217. let autoQueryBundleInfo = document.createElement('span');
  1218. autoQueryBundleInfo.setAttribute('style', 'margin-left: 15px');
  1219. autoQueryBundleInfo.setAttribute('title', '自动查询卡牌价格时是否同时查询补充包价格');
  1220. autoQueryBundleInfo.innerHTML = '查询补充包:';
  1221.  
  1222. let autoQueryBundle = document.createElement('input');
  1223. autoQueryBundle.setAttribute('type','checkbox')
  1224. autoQueryBundle.setAttribute('style','background-color: rgba( 103, 193, 245, 0.2 ); color: #fff; border: 1px solid #000;border-radius: 3px; width: 15px;padding: 5px;height:10px')
  1225. autoQueryBundle.value = 'checked';
  1226. autoQueryBundle.checked = boosterInfo.autoQueryBundle === undefined ? false : boosterInfo.autoQueryBundle;
  1227. autoQueryBundle.onclick = function (){
  1228. if (autoQueryBundle.checked) {
  1229. boosterInfo.autoQueryBundle = true;
  1230. } else {
  1231. boosterInfo.autoQueryBundle = false;
  1232. }
  1233. saveStorage(boosterKey,boosterInfo)
  1234. }
  1235.  
  1236. //查询间隔提示
  1237. let timeIntervalInfo = document.createElement('span');
  1238. timeIntervalInfo.setAttribute('style', 'margin-left: 10px');
  1239. timeIntervalInfo.setAttribute('title', '自动查询卡牌发送请求间隔,单位s,第一次查询建议不低于8。查询后会生成缓存,之后可以设置为3,加快速度又不会被封禁');
  1240. timeIntervalInfo.innerHTML = '请求间隔:';
  1241.  
  1242. //自定义查询时间间隔
  1243. let timeInterval = document.createElement('input');
  1244. timeInterval.setAttribute('id', 'timeInterval');
  1245. timeInterval.setAttribute('style', 'margin-left: 15px;background-color: rgba( 103, 193, 245, 0.2 ); color: #fff; border: 1px solid #000;border-radius: 3px; width: 30px;padding: 5px;');
  1246. timeInterval.value = boosterInfo.timeInterval;
  1247. timeInterval.onchange = function () {
  1248. doSearch();
  1249. };
  1250.  
  1251. gemsDiv.appendChild(boosterCost);
  1252. gemsDiv.appendChild(sourceSelect);
  1253. gemsDiv.appendChild(marketPriceInfo);
  1254. gemsDiv.appendChild(customPriceInfo);
  1255. gemsDiv.appendChild(gemPriceInput);
  1256. gemsDiv.appendChild(autoQueryBundleInfo);
  1257. gemsDiv.appendChild(autoQueryBundle);
  1258. gemsDiv.appendChild(timeIntervalInfo);
  1259. gemsDiv.appendChild(timeInterval);
  1260. gemsDiv.appendChild(autoQueryPrice);
  1261. // 0代表使用市场价,1代表使用自定义
  1262. if (boosterInfo.sourceIndex === 1) {
  1263. gemsSackPrice = customSack;
  1264. } else {
  1265. gemsSackPrice = marketSack;
  1266. }
  1267. if (boosterInfo.typeIndex === 3) {
  1268. gemsDiv.appendChild(blackButton);
  1269. }
  1270.  
  1271. //搜索输入框
  1272. let searchInput = document.createElement('input');
  1273. searchInput.onchange = function () {
  1274. doSearch()
  1275. };
  1276. searchInput.setAttribute('id', 'searchInput');
  1277. searchInput.setAttribute('style', 'background-color: rgba( 103, 193, 245, 0.2 ); color: #fff; border: 1px solid #000;border-radius: 3px; width: 240px;padding: 5px;');
  1278. if (searchInfo && searchInfo.trim() !== '') {
  1279. searchInput.value = searchInfo
  1280. }
  1281.  
  1282. let typeInfo = document.createElement('span');
  1283. typeInfo.setAttribute('style', 'margin-left: 30px');
  1284. typeInfo.innerHTML = '库选择:';
  1285. typeInfo.setAttribute('title', '选择从哪一个列表里面进行查询');
  1286.  
  1287.  
  1288. //搜索类型选择
  1289. let typeSelect = document.createElement('select');
  1290. let allOption = document.createElement('option');
  1291. allOption.setAttribute('value', 'all');
  1292. allOption.innerHTML = '全部';
  1293. let boosterOption = document.createElement('option');
  1294. boosterOption.setAttribute('value', 'booster');
  1295. boosterOption.innerHTML = '队列';
  1296. let filterOption = document.createElement('option');
  1297. filterOption.setAttribute('value', 'filter');
  1298. filterOption.innerHTML = '收藏';
  1299. let outOption = document.createElement('option');
  1300. outOption.setAttribute('value', 'out');
  1301. outOption.innerHTML = '未收藏';
  1302. let blackOption = document.createElement('option');
  1303. blackOption.setAttribute('value', 'black');
  1304. blackOption.innerHTML = '黑名单';
  1305.  
  1306. typeSelect.add(allOption);
  1307. typeSelect.add(boosterOption);
  1308. typeSelect.add(filterOption);
  1309. typeSelect.add(outOption);
  1310. typeSelect.add(blackOption);
  1311.  
  1312. typeSelect.selectedIndex = boosterInfo.typeIndex;
  1313. typeSelect.setAttribute('style', 'margin-left:30px;width:100px');
  1314. typeSelect.setAttribute('id', 'typeSelect');
  1315. typeSelect.onchange = function () {
  1316. doSearch();
  1317. };
  1318.  
  1319. let pageSizeInfo = document.createElement('span');
  1320. pageSizeInfo.setAttribute('style', 'margin-left: 30px');
  1321. pageSizeInfo.innerHTML = '每页数量:';
  1322. pageSizeInfo.setAttribute('title', '每页展示的数据量,输入1-50的数字,点击搜索按钮后生效');
  1323.  
  1324. //页面size输入框
  1325. let pageSizeInput = document.createElement('input');
  1326. pageSizeInput.setAttribute('id', 'pageSizeInput');
  1327. pageSizeInput.setAttribute('style', 'margin-left: 15px;background-color: rgba( 103, 193, 245, 0.2 ); color: #fff; border: 1px solid #000;border-radius: 3px; width: 60px;padding: 5px;');
  1328. pageSizeInput.setAttribute('title', '输入每页数量,默认10');
  1329. pageSizeInput.value = pageSize;
  1330. pageSizeInput.onchange = function () {
  1331. doSearch();
  1332. };
  1333.  
  1334. //搜索按钮
  1335. let searchButton = document.createElement('button');
  1336. searchButton.setAttribute('id', 'searchButton');
  1337. searchButton.innerHTML = '搜索';
  1338. searchButton.setAttribute('style', 'border-radius: 2px; border: none;padding: 1px;cursor: pointer;color: #67c1f5 !important;background: rgba( 103, 193, 245, 0.2 );height: 26px;width: 90px;float: right;margin-bottom: 32px;');
  1339. searchButton.onclick = function () {
  1340. doSearch()
  1341. };
  1342. gameForm.appendChild(gemsDiv);
  1343. gameForm.appendChild(searchInput);
  1344. gameForm.appendChild(typeInfo);
  1345. gameForm.appendChild(typeSelect);
  1346. gameForm.appendChild(pageSizeInfo);
  1347. gameForm.appendChild(pageSizeInput)
  1348. gameForm.appendChild(searchButton);
  1349.  
  1350. let table = document.createElement('table');
  1351. table.setAttribute('style', 'width: 100%;overflow-x:auto;white-space: nowrap');
  1352. let th1 = document.createElement('th');
  1353. th1.innerHTML = '游戏';
  1354. th1.setAttribute('style', 'width:122px');
  1355. th1.setAttribute('title', '点击可以跳转到市场对应游戏的物品列表');
  1356. let th2 = document.createElement('th');
  1357. th2.innerHTML = '名称';
  1358. th2.setAttribute('style', 'width:150px');
  1359. let th3 = document.createElement('th');
  1360. th3.innerHTML = '状态';
  1361. th3.setAttribute('style', 'width:80px');
  1362. let th4 = document.createElement('th');
  1363. th4.innerHTML = '宝石';
  1364. th4.setAttribute('style', 'width:80px');
  1365. th4.setAttribute('title', '此游戏制作一个补充包需要的宝石数量(此游戏总卡牌数量)');
  1366. let th5 = document.createElement('th');
  1367. th5.innerHTML = '成本';
  1368. th5.setAttribute('style', 'width:40px');
  1369. th5.setAttribute('title', '按照直接购买宝石价格计算');
  1370. let th6 = document.createElement('th');
  1371. th6.innerHTML = '均价';
  1372. th6.setAttribute('style', 'width:40px');
  1373. th6.setAttribute('title', '拆包后三张普通卡牌均价(最低售价计算,税后)');
  1374. let th7 = document.createElement('th');
  1375. th7.innerHTML = '卖单';
  1376. th7.setAttribute('style', 'width:40px');
  1377. th7.setAttribute('title', '按最低卖单卖出税后收入,按照15%税率粗略计算,高于成本会变黄');
  1378. let th8 = document.createElement('th');
  1379. th8.innerHTML = '买单';
  1380. th8.setAttribute('style', 'width:40px');
  1381. th8.setAttribute('title', '按买单直接卖出税后收入,按照15%税率粗略计算,高于成本会变黄');
  1382. let th9 = document.createElement('th');
  1383. th9.innerHTML = '利润率';
  1384. th9.setAttribute('style', 'width:60px');
  1385. th9.setAttribute('title', '三张卡牌总价税后 / 成本 ');
  1386. let th10 = document.createElement('th');
  1387. th10.innerHTML = '销量';
  1388. th10.setAttribute('style', 'width:40px');
  1389. th10.setAttribute('title', '补充包日销量');
  1390. let th11 = document.createElement('th');
  1391. // th11.setAttribute('style', 'position: sticky;right: 0');
  1392. th11.innerHTML = '操作';
  1393.  
  1394. let th12 = document.createElement('th');
  1395. th12.setAttribute('style', 'width:40px');
  1396. th12.setAttribute('title', '此包历史制作次数 ');
  1397. th12.innerHTML = '制作量';
  1398.  
  1399. let th13 = document.createElement('th');
  1400. th13.setAttribute('style', 'width:40px');
  1401. th13.setAttribute('title', '补充包最高买单税后价格 ');
  1402. th13.innerHTML = '包价';
  1403.  
  1404. let th14 = document.createElement('th');
  1405. th14.setAttribute('style', 'width:40px');
  1406. th14.setAttribute('title', '上次制作补充包的时间');
  1407. th14.innerHTML = '上次制作';
  1408.  
  1409. let th15 = document.createElement('th');
  1410. th15.innerHTML = '平均卖单量';
  1411. th15.setAttribute('style', 'width:40px');
  1412. th15.setAttribute('title', '该游戏所有普通卡牌平均卖单数量');
  1413.  
  1414. let tableHead = document.createElement('thead');
  1415. let tableHeadTr = document.createElement('tr');
  1416.  
  1417. tableHeadTr.appendChild(th1);
  1418. tableHeadTr.appendChild(th2);
  1419. tableHeadTr.appendChild(th3);
  1420. tableHeadTr.appendChild(th4);
  1421. tableHeadTr.appendChild(th5);
  1422. tableHeadTr.appendChild(th6);
  1423. tableHeadTr.appendChild(th15);
  1424. // thead.appendChild(th7);
  1425. // thead.appendChild(th8);
  1426. tableHeadTr.appendChild(th9);
  1427. tableHeadTr.appendChild(th13);
  1428. tableHeadTr.appendChild(th10);
  1429. tableHeadTr.appendChild(th12);
  1430. tableHeadTr.appendChild(th14);
  1431. tableHeadTr.appendChild(th11);
  1432.  
  1433. tableHead.appendChild(tableHeadTr);
  1434. table.appendChild(tableHead);
  1435. let tbody = document.createElement('tbody');
  1436. tbody.setAttribute('style','width: 100%;overflow-x:auto;')
  1437.  
  1438. if (searchResult.length > 0) {
  1439. for (let i = startIndex; i < searchResult.length && i < startIndex + pageSize; i++) {
  1440. let item = searchResult[i];
  1441. let tr = document.createElement('tr');
  1442. tr.setAttribute('style', 'height:60px');
  1443.  
  1444. //游戏缩略图,点击跳转到市场
  1445. let img = document.createElement('img');
  1446. img.setAttribute('src', 'https://steamcdn-a.akamaihd.net/steam/apps/' + item.appid + '/capsule_sm_120.jpg');
  1447. let imgDiv = document.createElement('a');
  1448. imgDiv.appendChild(img)
  1449.  
  1450. //游戏名称
  1451. let name = document.createElement('a');
  1452. name.innerHTML = item.name;
  1453. name.setAttribute('style', 'display: inline-block;overflow: hidden;text-overflow: ellipsis;max-width: 135px;color:#8F98A0');
  1454.  
  1455. //制作冷却
  1456. let availableTime = document.createElement('td');
  1457. let numberTest = /[0-9]/;
  1458. if (!item.available_at_time) {
  1459. if (cacheInfo.cardInfo[item.appid] && cacheInfo.cardInfo[item.appid].marketable !== undefined && !cacheInfo.cardInfo[item.appid].marketable) {
  1460. availableTime.innerHTML = '不可交易';
  1461. availableTime.setAttribute('title', '此游戏卡牌已不可在市场交易');
  1462. availableTime.setAttribute('style', 'color:red')
  1463. } else {
  1464. availableTime.innerHTML = '可制作';
  1465. availableTime.setAttribute('title', '点击制作此游戏补充包');
  1466. availableTime.setAttribute('style', 'color:yellow;text-decoration:underline;cursor: pointer')
  1467. availableTime.onclick = function () {
  1468. createSingleBooster(item)
  1469. }
  1470. }
  1471. } else if (numberTest.test(item.available_at_time)) {
  1472. availableTime.innerHTML = item.available_at_time;
  1473. availableTime.setAttribute('title', '下次可制作补充包时间');
  1474. } else {
  1475. availableTime.innerHTML = item.available_at_time;
  1476. }
  1477.  
  1478. //补充包需要宝石数
  1479. let price = document.createElement('td');
  1480. price.innerHTML = item['price'] + "(" + gemsCountToCardCount[item['price']] + ")";
  1481.  
  1482. //制作成本
  1483. let cost = document.createElement('td');
  1484. let costPrice = 0.00;
  1485. if (gemsSackPrice > 0) {
  1486. let tempCost = item['price'] / 1000 * gemsSackPrice;
  1487. if (!isNaN(tempCost)) {
  1488. costPrice = parseFloat(tempCost.toFixed(2));
  1489. cost.innerHTML = costPrice.toString()
  1490. }
  1491. }
  1492. let priceTd = document.createElement('td');
  1493. priceTd.appendChild(price)
  1494.  
  1495. //最低卖单价格
  1496. let sellPrice = document.createElement('td');
  1497. sellPrice.setAttribute('title', '市场最低售价税后收入,高于成本会变黄');
  1498.  
  1499. //补充包最高买单价格
  1500. let bundleBuyPrice = document.createElement('td');
  1501. bundleBuyPrice.setAttribute('title', '市场最高买价税后收入,高于成本会变黄');
  1502.  
  1503. //利润率,(最低卖单税后-成本)/ 成本
  1504. let profitRate = document.createElement('td');
  1505. profitRate.setAttribute('title', '利润率,(最低卖单税后-成本)/ 成本');
  1506.  
  1507. //补充包日销量
  1508. let bundleSoldCount = document.createElement('td');
  1509.  
  1510. //税后三张卡牌均价如果高于成本,红色
  1511. let cardPrice = document.createElement('td');
  1512. let sellerCount = document.createElement('td');
  1513. if (!getMarketable(item.appid)) {
  1514. cardPrice.innerHTML = '无';
  1515. } else if (priceData[item.appid] && priceData[item.appid].hadQueryCard) {
  1516. let tempCardPrice = priceData[item.appid].cardPrice;
  1517. if (!isNaN(tempCardPrice)) {
  1518. tempCardPrice = (tempCardPrice / 1.15).toFixed(2);
  1519. cardPrice.innerHTML = tempCardPrice;
  1520. if (costPrice > 0 && tempCardPrice > costPrice) {
  1521. cardPrice.setAttribute('style', 'color:red');
  1522. }
  1523. //如果有成本价,渲染利润率
  1524. if (costPrice > 0) {
  1525. let rate = (tempCardPrice - costPrice) / costPrice;
  1526. profitRate.innerHTML = (Math.round(rate * 10000) / 100).toFixed(2) + '%';
  1527. if (rate > 0) {
  1528. profitRate.setAttribute('style', 'color:red')
  1529. }
  1530. }
  1531. } else {
  1532. cardPrice.innerHTML = '未知';
  1533. }
  1534.  
  1535. if (!isNaN(priceData[item.appid].aveargeSellerCount)) {
  1536. sellerCount.innerHTML = priceData[item.appid].aveargeSellerCount.toFixed(2);
  1537. }
  1538. } else {
  1539. cardPrice.innerHTML = '查询';
  1540. cardPrice.setAttribute('title', '查询请求较多,点击后需要稍等片刻');
  1541. cardPrice.setAttribute('style', 'text-decoration:underline;cursor: pointer');
  1542. cardPrice.onclick = function () {
  1543. computeCardPrice(item.appid)
  1544. };
  1545. }
  1546.  
  1547. //如果已查询补充包价格,渲染
  1548. if (!getMarketable(item.appid)) {
  1549. bundleBuyPrice.innerHTML = '无';
  1550. } else if (priceData[item.appid] && priceData[item.appid].hadBooster) {
  1551. let boosterBuyPrice = priceData[item.appid].buyPrice;
  1552. bundleSoldCount.innerHTML = priceData[item.appid].sold !== undefined ? priceData[item.appid].sold : 0;
  1553. if (!isNaN(boosterBuyPrice)) {
  1554. boosterBuyPrice = (boosterBuyPrice / 1.15).toFixed(2);
  1555. bundleBuyPrice.innerHTML = boosterBuyPrice;
  1556. if (costPrice > 0 && boosterBuyPrice > costPrice) {
  1557. bundleBuyPrice.setAttribute('style', 'color:yellow');
  1558. }
  1559. } else {
  1560. bundleBuyPrice.innerHTML = '未知';
  1561. }
  1562. } else {
  1563. bundleBuyPrice.innerHTML = '查询';
  1564. bundleBuyPrice.setAttribute('title', '查询当前补充包最高买单价格,展示税后');
  1565. bundleBuyPrice.setAttribute('style', 'text-decoration:underline;cursor: pointer');
  1566. bundleBuyPrice.onclick = function () {
  1567. querySingleBoosterPrice(item)
  1568. };
  1569. }
  1570.  
  1571. //历史制作总量
  1572. let madeCount = document.createElement('td');
  1573. let lastMade = document.createElement('td');
  1574. if (cacheInfo.cardInfo[item.appid]) {
  1575. if (cacheInfo.cardInfo[item.appid].madeCount) {
  1576. madeCount.innerHTML = cacheInfo.cardInfo[item.appid].madeCount;
  1577. } else {
  1578. madeCount.innerHTML = 0;
  1579. }
  1580. if (cacheInfo.cardInfo[item.appid].lastMade){
  1581. lastMade.innerHTML = cacheInfo.cardInfo[item.appid].lastMade
  1582. }
  1583. } else {
  1584. madeCount.innerHTML = 0;
  1585. }
  1586.  
  1587. //收藏、移除、移入收藏、加入队列、彻底删除等操作
  1588. let button1;
  1589. let button2;
  1590. let button3;
  1591. if (boosterInfo.game.indexOf(item.appid.toString()) > -1) {
  1592. button1 = generateOperateButton(item, 'boosterToCollect');
  1593. button2 = generateOperateButton(item, 'boosterToOut')
  1594. } else if (boosterInfo.collect.indexOf(item.appid.toString()) > -1) {
  1595. button1 = generateOperateButton(item, 'collectToBooster');
  1596. button2 = generateOperateButton(item, 'collectToOut')
  1597. } else if (boosterInfo.black.indexOf(item.appid.toString()) > -1) {
  1598. button3 = generateOperateButton(item, 'blackToOut');
  1599. } else {
  1600. button1 = generateOperateButton(item, 'outToBooster');
  1601. button2 = generateOperateButton(item, 'outToCollect');
  1602. button3 = generateOperateButton(item, 'outToBlack');
  1603.  
  1604. }
  1605. let button4 = generateOperateButton(item, 'reset');
  1606.  
  1607. let operate = document.createElement('td');
  1608. // operate.setAttribute('style', 'position: sticky;right: 0');
  1609.  
  1610. tr.appendChild(generateTd(imgDiv,'点击进入此游戏物品的市场页面',buildMarketUrl(item.name,item.appid)));
  1611. tr.appendChild(generateTd(name,item.name,'https://store.steampowered.com/app/' + item.appid));
  1612. tr.appendChild(generateTd(availableTime,undefined,undefined));
  1613. tr.appendChild(generateTd(price,undefined,undefined));
  1614. tr.appendChild(generateTd(cost,undefined,undefined));
  1615. tr.appendChild(generateTd(cardPrice,undefined,undefined))
  1616. tr.appendChild(generateTd(sellerCount,undefined,undefined))
  1617. // tr.appendChild(sellPrice);
  1618. tr.appendChild(generateTd(profitRate,undefined,undefined));
  1619. tr.appendChild(generateTd(bundleBuyPrice,undefined,undefined));
  1620. tr.appendChild(generateTd(bundleSoldCount,'补充包日销量',undefined));
  1621. tr.appendChild(generateTd(madeCount,'有记录的此游戏做包总次数',undefined));
  1622. tr.appendChild(generateTd(lastMade,'上次制作时间',undefined));
  1623. if (button1) {
  1624. operate.appendChild(button1);
  1625. }
  1626. if (button2) {
  1627. operate.appendChild(button2)
  1628. }
  1629. if (button3) {
  1630. operate.appendChild(button3)
  1631. }
  1632. operate.appendChild(button4)
  1633. tr.appendChild(operate);
  1634. tbody.appendChild(tr)
  1635. }
  1636. }
  1637. table.appendChild(tbody);
  1638.  
  1639. let tableDiv = document.createElement('div');
  1640. tableDiv.setAttribute('style','width:860px;overflow-x: auto;')
  1641. tableDiv.appendChild(table)
  1642. gameForm.appendChild(tableDiv);
  1643.  
  1644. //计算页数
  1645. totalCount = Math.ceil(searchResult.length / pageSize);
  1646.  
  1647. //上一页按钮
  1648. let beforeButton = document.createElement('button');
  1649. beforeButton.setAttribute('id', 'beforeButton');
  1650. beforeButton.innerHTML = '上一页';
  1651.  
  1652. beforeButton.setAttribute('class', pageNum === 1 ? classObj.disableButton : classObj.enableButton);
  1653. beforeButton.setAttribute('style', 'height: 25px;margin-right: 30px;width: 80px;');
  1654. beforeButton.onclick = function () {
  1655. beforePage()
  1656. };
  1657. gameForm.appendChild(beforeButton);
  1658.  
  1659. let pageSpan = document.createElement('span');
  1660. pageSpan.innerHTML = '共 ' + searchResult.length + ' 个结果, ' + pageNum + ' / ' + totalCount;
  1661. gameForm.appendChild(pageSpan);
  1662.  
  1663. //下一页按钮
  1664. let afterButton = document.createElement('button');
  1665. afterButton.setAttribute('id', 'afterButton');
  1666. afterButton.innerHTML = '下一页';
  1667. afterButton.setAttribute('class', pageNum === totalCount ? classObj.disableButton : classObj.enableButton);
  1668. afterButton.setAttribute('style', 'height: 25px;margin-left: 30px;width: 80px;');
  1669. afterButton.onclick = function () {
  1670. afterPage()
  1671. };
  1672. gameForm.appendChild(afterButton);
  1673.  
  1674. //跳转页输入
  1675. let jumpInput = document.createElement('input');
  1676. jumpInput.setAttribute('id', 'jumpInput');
  1677. jumpInput.setAttribute('style', 'background-color: rgba( 103, 193, 245, 0.2 );color: #fff;border: 1px solid #000;border-radius: 3px;width: 60px;padding: 5px;margin-left: 30px;');
  1678. jumpInput.onchange = function () {
  1679. jumpPage();
  1680. };
  1681. gameForm.appendChild(jumpInput);
  1682. //跳转按钮
  1683. let jumpButton = document.createElement('button');
  1684. jumpButton.setAttribute('id', 'jumpButton');
  1685. jumpButton.innerHTML = '跳转';
  1686. jumpButton.setAttribute('class', classObj.enableButton);
  1687. jumpButton.setAttribute('style', 'height: 25px;margin-left: 30px;width: 80px;');
  1688. jumpButton.onclick = function () {
  1689. jumpPage();
  1690. };
  1691. gameForm.appendChild(jumpButton);
  1692. }
  1693.  
  1694. function generateTd(element,title,link){
  1695. let tempTd = document.createElement('td');
  1696. tempTd.appendChild(element)
  1697. if (title !== undefined){
  1698. tempTd.setAttribute('title',title)
  1699. }
  1700. if (link !== undefined){
  1701. element.setAttribute('href',link)
  1702. element.setAttribute('target','_blank')
  1703. }
  1704. return tempTd
  1705. }
  1706.  
  1707. //生成操作按钮
  1708. function generateOperateButton(item, type) {
  1709. let button = document.createElement('button');
  1710. button.innerHTML = operateButton[type].text;
  1711. button.setAttribute('class', classObj.enableButton);
  1712. button.setAttribute('style', operateButton[type].style);
  1713. button.setAttribute('id', item.appid.toString() + '+' + type);
  1714. button.setAttribute('title', operateButton[type].desc);
  1715. button.onclick = function () {
  1716. operateGame(item.appid.toString(), type)
  1717. };
  1718. return button
  1719. }
  1720.  
  1721. //跳转到指定页
  1722. function jumpPage() {
  1723. let jumpNum = document.getElementById('jumpInput').value;
  1724. if (isNaN(jumpNum) || stringBlank(jumpNum) || parseInt(jumpNum) < 1) {
  1725. return
  1726. }
  1727. pageNum = parseInt(jumpNum);
  1728. if (pageNum > totalCount) {
  1729. pageNum = 1
  1730. }
  1731. document.getElementById('jumpInput').value = '';
  1732. generateGameList(pageNum, pageSize, searchResult)
  1733. }
  1734.  
  1735. //上一页
  1736. function beforePage() {
  1737. if (pageNum < 2) {
  1738. return
  1739. }
  1740. pageNum = pageNum - 1;
  1741. generateGameList(pageNum, pageSize, searchResult)
  1742. }
  1743.  
  1744. //下一页
  1745. function afterPage() {
  1746. if (pageNum > totalCount - 1) {
  1747. return
  1748. }
  1749. pageNum = pageNum + 1;
  1750. generateGameList(pageNum, pageSize, searchResult)
  1751. }
  1752.  
  1753. //生成一键做包等
  1754. function generateCreateButton() {
  1755. //每次删除后重新创建
  1756. let tempCreate = document.getElementById('createButton');
  1757. if (tempCreate !== null) {
  1758. tempCreate.parentNode.removeChild(tempCreate)
  1759. }
  1760. let tempConvert = document.getElementById('convertButton');
  1761. if (tempConvert !== null) {
  1762. tempConvert.parentNode.removeChild(tempConvert)
  1763. }
  1764.  
  1765. //更新下拉列表
  1766. // noinspection JSAnnotator
  1767. gameSelector.options.length = 0;
  1768. if (boosterInfo.filter) {
  1769. boosterOptions.map((item) => {
  1770. gameSelector.add(item)
  1771. })
  1772. } else {
  1773. backUpOptions.map((item) => {
  1774. gameSelector.add(item)
  1775. })
  1776. }
  1777.  
  1778. //绘制创建按钮
  1779. let createButton = document.createElement('button');
  1780. createButton.setAttribute('title', '只操作补充包队列的游戏(并且自动跳过其中不可交易的游戏)');
  1781. createButton.setAttribute('id', 'createButton');
  1782. createButton.onclick = function () {
  1783. document.getElementById("createButton").setAttribute('class', classObj.disableButton);
  1784. doneList = [];
  1785. createBooster(0)
  1786. };
  1787. let totalCost = countGemsCost();
  1788. if (availableGame.length === 0 || totalCost === 0) {
  1789. createButton.innerHTML = '队列全部冷却中';
  1790. createButton.setAttribute('class', classObj.disableButton)
  1791. } else {
  1792. createButton.innerHTML = '一键制作 ' + availableGame.length + ' 个补充包' + ' ( ' + totalCost + ' ) ';
  1793. createButton.setAttribute('class', classObj.enableButton)
  1794. }
  1795. createButton.setAttribute('style', 'height: 29px; margin-top: 16px;width: 208px;');
  1796. document.getElementsByClassName('booster_game_selector')[0].appendChild(createButton);
  1797. //绘制转换按钮
  1798. let convertButton = document.createElement('button');
  1799. convertButton.setAttribute('id', 'convertButton');
  1800. convertButton.setAttribute('class', classObj.enableButton);
  1801. convertButton.innerHTML = boosterInfo.filter ? '展示全部' : '展示队列';
  1802. convertButton.setAttribute('style', 'height: 29px; margin-top: 16px;width: 80px;margin-left:12px');
  1803. convertButton.onclick = function () {
  1804. boosterInfo.filter = !boosterInfo.filter;
  1805. saveStorage(boosterKey, boosterInfo);
  1806. generateCreateButton()
  1807. };
  1808. document.getElementsByClassName('booster_game_selector')[0].appendChild(convertButton);
  1809. }
  1810.  
  1811. function notice(message,time) {
  1812. let noticeElement = document.getElementById("notice");
  1813. noticeElement.textContent = message;
  1814. noticeElement.style.display = "block";
  1815. setTimeout(function() {
  1816. noticeElement.style.display = "none";
  1817. }, time === undefined ? 1000 : time);
  1818. }
  1819.  
  1820.  
  1821. //生成常用工具栏
  1822. function generateCommonTool() {
  1823. let toolWindow = document.createElement('div');
  1824. toolWindow.setAttribute('id', 'toolWindow');
  1825. toolWindow.setAttribute('style', 'height:40px;width:100%');
  1826.  
  1827. //常用跳转链接
  1828. let toInventory = generateToolLink('我的库存', document.getElementsByClassName('goostatus_right')[0].childNodes[3].childNodes[1].getAttribute('href'), 8);
  1829. let toMarket = generateToolLink('市场', 'https://steamcommunity.com/market/', 18);
  1830. let steamdb = generateToolLink('steamdb', 'https://steamdb.info', 18);
  1831. let keylol = generateToolLink('其乐', 'https://keylol.com', 18);
  1832. let exportData = generateToolLink('导出', undefined, 18);
  1833. exportData.setAttribute('title', '导出补充包配置数据到剪贴板')
  1834. exportData.onclick = function () {
  1835. let totalData = {}
  1836. totalData[boosterKey] = getStorage(boosterKey)
  1837. totalData[cacheKey] = getStorage(cacheKey)
  1838. navigator.clipboard.writeText(JSON.stringify(totalData))
  1839. notice('补充包数据已复制到剪贴板',1000)
  1840. }
  1841. let importData = generateToolLink('导入', undefined, 18);
  1842. importData.setAttribute('title', '导入补充包数据配置,会覆盖现有数据')
  1843. importData.onclick = function () {
  1844. let data = prompt('请粘贴补充包数据');
  1845. try {
  1846. if (data != null) {
  1847. let totalData = JSON.parse(data);
  1848. if (totalData[boosterKey] !== undefined) {
  1849. boosterInfo = totalData[boosterKey];
  1850. saveStorage(boosterKey, boosterInfo);
  1851. }
  1852. if (totalData[cacheKey] !== undefined) {
  1853. cacheInfo = totalData[cacheKey];
  1854. saveStorage(cacheKey, cacheInfo);
  1855. }
  1856. notice('导入成功,请刷新页面',1000)
  1857. } else {
  1858. notice('未输入任何信息',1000)
  1859. }
  1860. } catch (e) {
  1861. alert("输入格式不合法,请参考导出功能数据")
  1862. }
  1863. }
  1864.  
  1865. toolWindow.append(toInventory);
  1866. toolWindow.append(toMarket);
  1867. toolWindow.append(steamdb);
  1868. toolWindow.append(keylol);
  1869. toolWindow.append(exportData);
  1870. toolWindow.append(importData);
  1871.  
  1872. boosterPage.insertBefore(toolWindow, gameForm)
  1873. }
  1874.  
  1875.  
  1876.  
  1877.  
  1878. /**
  1879. * 生成跳转小工具
  1880. * @param name 名称
  1881. * @param link 目标地址
  1882. * @param left 左侧margin
  1883. * @returns {HTMLAnchorElement}
  1884. */
  1885. function generateToolLink(name, link, left) {
  1886. let aTag = document.createElement('a');
  1887. aTag.innerHTML = name;
  1888. aTag.setAttribute('style', 'color:green;text-decoration:underline;margin-left:' + left + 'px');
  1889. if (link !== undefined) {
  1890. aTag.setAttribute('href', link);
  1891. aTag.setAttribute('target', '_blank');
  1892. }
  1893. return aTag;
  1894. }
  1895.  
  1896.  
  1897. //计算一键做包需要的宝石数
  1898. function countGemsCost() {
  1899. let totalCost = 0;
  1900. if (availableGame.length > 0) {
  1901. availableGame.map(function (item) {
  1902. if (getMarketable(item.value) && GAME_INFO[item.value] && GAME_INFO[item.value]['price']) {
  1903. totalCost += parseInt(GAME_INFO[item.value]['price'])
  1904. }
  1905. })
  1906. }
  1907. return totalCost
  1908. }
  1909.  
  1910. //初始化/收藏/移除等操作时,重新构建下拉数据
  1911. function buildOptions() {
  1912. collectOptions = [];
  1913. boosterOptions = [];
  1914. availableGame = [];
  1915. outOptions = [];
  1916. blackOptions = [];
  1917. for (let i = 0; i < backUpOptions.length; i++) {
  1918. let item = backUpOptions[i];
  1919. if (item.value) {
  1920. if (boosterInfo.game.indexOf(item.value) > -1) {
  1921. boosterOptions.push(item);
  1922. if (item.getAttribute("class") === "available") {
  1923. availableGame.push(item)
  1924. }
  1925. } else if (boosterInfo.collect.indexOf(item.value) > -1) {
  1926. collectOptions.push(item)
  1927. } else if (boosterInfo.black.indexOf(item.value) > -1) {
  1928. blackOptions.push(item)
  1929. } else {
  1930. outOptions.push(item)
  1931. }
  1932. }
  1933. }
  1934. }
  1935.  
  1936. //对游戏的收藏、删除等操作
  1937. function operateGame(appid, type) {
  1938. switch (type) {
  1939. case 'outToCollect':
  1940. if (boosterInfo.collect.indexOf(appid) > -1) {
  1941. return
  1942. } else {
  1943. boosterInfo.collect.push(appid)
  1944. }
  1945. break;
  1946. case 'outToBooster':
  1947. if (boosterInfo.game.indexOf(appid) > -1) {
  1948. return
  1949. } else {
  1950. boosterInfo.game.push(appid)
  1951. }
  1952. break;
  1953. case 'collectToOut':
  1954. if (boosterInfo.collect.indexOf(appid) === -1) {
  1955. return
  1956. } else {
  1957. boosterInfo.collect.splice(boosterInfo.collect.indexOf(appid), 1)
  1958. }
  1959. break;
  1960. case 'collectToBooster':
  1961. if (boosterInfo.collect.indexOf(appid) === -1) {
  1962. return
  1963. } else {
  1964. boosterInfo.collect.splice(boosterInfo.collect.indexOf(appid), 1);
  1965. boosterInfo.game.push(appid)
  1966. }
  1967. break;
  1968. case 'boosterToOut':
  1969. if (boosterInfo.game.indexOf(appid) === -1) {
  1970. return
  1971. } else {
  1972. boosterInfo.game.splice(boosterInfo.game.indexOf(appid), 1)
  1973. }
  1974. break;
  1975. case 'boosterToCollect':
  1976. if (boosterInfo.game.indexOf(appid) === -1) {
  1977. return
  1978. } else {
  1979. boosterInfo.game.splice(boosterInfo.game.indexOf(appid), 1);
  1980. boosterInfo.collect.push(appid)
  1981. }
  1982. break;
  1983. case 'outToBlack':
  1984. if (boosterInfo.black.indexOf(appid) > -1) {
  1985. return
  1986. } else {
  1987. boosterInfo.black.push(appid)
  1988. }
  1989. break;
  1990. case 'blackToOut':
  1991. if (boosterInfo.black.indexOf(appid) === -1) {
  1992. return
  1993. } else {
  1994. boosterInfo.black.splice(boosterInfo.black.indexOf(appid), 1);
  1995. }
  1996. break;
  1997. case 'reset':
  1998. cacheInfo.cardInfo[appid] = undefined;
  1999. saveStorage(cacheKey, cacheInfo)
  2000. break;
  2001. default:
  2002. return
  2003. }
  2004. saveStorage(boosterKey, boosterInfo);
  2005. //刷新下拉列表,重新生成按钮和表单
  2006. buildOptions();
  2007. generateCreateButton();
  2008. generateGameList(pageNum, pageSize, searchResult)
  2009. }
  2010.  
  2011. //从localStorage取值,判断是不是合法的json,如果不是,清除缓存
  2012. function getStorage(key) {
  2013. let config = localStorage.getItem(key);
  2014. try {
  2015. if (typeof JSON.parse(config) == "object") {
  2016. return JSON.parse(config);
  2017. } else {
  2018. localStorage.removeItem(key);
  2019. return null;
  2020. }
  2021. } catch (e) {
  2022. localStorage.removeItem(key);
  2023. return null;
  2024. }
  2025. }
  2026.  
  2027. //将值存入从localStorage
  2028. function saveStorage(key, item) {
  2029. localStorage.setItem(key, JSON.stringify(item))
  2030. }
  2031.  
  2032. //补充包页面初始化
  2033. function initBooster() {
  2034. //初始化css
  2035. let domStyle = document.createElement('style');
  2036. domStyle.type = 'text/css';
  2037. domStyle.rel = 'stylesheet';
  2038. domStyle.appendChild(document.createTextNode(initStyle()));
  2039. boosterPage.appendChild(domStyle);
  2040.  
  2041. GAME_INFO = CBoosterCreatorPage.sm_rgBoosterData;
  2042. //没有可以做补充包的游戏,直接返回
  2043. if (!gameSelector || gameSelector.length === 0) {
  2044. return
  2045. }
  2046. let selectOption = document.getElementsByClassName("booster_option")
  2047. if (selectOption) {
  2048. selectOption[0].parentNode.removeChild(selectOption[0])
  2049. }
  2050. //查询宝珠价格,用于计算成本
  2051. getGemsSackPrice();
  2052. //初始化可直接根据appid搜索物品的游戏列表
  2053. getEnableSearchAppList();
  2054.  
  2055. //删除默认展示
  2056. for (let i = gameForm.childNodes.length - 1; i >= 0; i--) {
  2057. gameForm.removeChild(gameForm.childNodes[i]);
  2058. }
  2059. //删除下拉列表第一个‘请选择’
  2060. if (stringBlank(gameSelector.options[0].value)) {
  2061. gameSelector.options.remove(0)
  2062. }
  2063. //从localStorage取用户自定义值,默认为空
  2064. boosterInfo = getStorage(boosterKey);
  2065. if (!boosterInfo) {
  2066. boosterInfo = {
  2067. game: [],
  2068. collect: [],
  2069. black: [],
  2070. filter: true,
  2071. typeIndex: 0,
  2072. sourceIndex: 0,
  2073. customPrice: 2.7,
  2074. timeInterval: 8,
  2075. autoQueryBundle : false
  2076. }
  2077. }
  2078. if (!boosterInfo.game) {
  2079. boosterInfo.game = []
  2080. }
  2081. if (!boosterInfo.collect) {
  2082. boosterInfo.collect = []
  2083. }
  2084. if (!boosterInfo.black) {
  2085. boosterInfo.black = []
  2086. }
  2087. if (boosterInfo.pageSize) {
  2088. pageSize = parseInt(boosterInfo.pageSize)
  2089. }
  2090. if (boosterInfo.customPrice) {
  2091. customSack = boosterInfo.customPrice
  2092. } else {
  2093. boosterInfo.customPrice = 1.6
  2094. }
  2095. if (boosterInfo.timeInterval) {
  2096. interval = boosterInfo.timeInterval
  2097. } else {
  2098. boosterInfo.timeInterval = 8
  2099. }
  2100.  
  2101. //默认将所有信息加入搜索结果
  2102. for (let key in GAME_INFO) {
  2103. searchResult.push(GAME_INFO[key])
  2104. }
  2105. //每个游戏都放入backUpOptions
  2106. for (let i = 0; i < gameSelector.length; i++) {
  2107. backUpOptions.push(gameSelector.options[i])
  2108. }
  2109.  
  2110. //构建collectOptions、boosterOptions、availableGame等
  2111. buildOptions();
  2112. //生成一键制作补充包等按钮
  2113. generateCreateButton();
  2114. //生成常用工具栏
  2115. generateCommonTool();
  2116. //生成下部游戏列表展示
  2117. generateGameList(pageNum, pageSize, searchResult);
  2118. //如果保存的搜索类型不是默认,首先执行一次搜索
  2119. if (boosterInfo.typeIndex !== 0) {
  2120. doSearch()
  2121. }
  2122. //生成提示框div
  2123. let notice = document.createElement('div')
  2124. notice.setAttribute('id', 'notice');
  2125. notice.setAttribute('style', 'position:fixed;top:50%;left:50%;transform: translate(-50%, -50%);padding: 15px 20px;color: orange;border-radius: 5px;display: none;');
  2126. boosterPage.append(notice)
  2127. }
  2128.  
  2129. //游戏详情页面初始化
  2130. function initApp() {
  2131. //查询宝珠价格,用于计算成本
  2132. getGemsSackPrice();
  2133. if (cacheInfo && cacheInfo.cardInfo && cacheInfo.cardInfo[currentAppId] && cacheInfo.cardInfo[currentAppId].name) {
  2134. currentAppName = cacheInfo.cardInfo[currentAppId].name;
  2135. }
  2136. }
  2137.  
  2138. //全局style修改
  2139. function initStyle() {
  2140. return `
  2141. ::-webkit-scrollbar{
  2142. height:30px
  2143. }
  2144. `
  2145. }
  2146.  
  2147. //初始化数据
  2148. function init() {
  2149. console.info("匹配到脚本")
  2150.  
  2151. //取缓存的游戏对应卡牌id值,减少查询缓存
  2152. if (getStorage(cacheKey) == null) {
  2153. cacheInfo = {};
  2154. cacheInfo.cardInfo = {};
  2155. cacheInfo.areaInfo = {};
  2156. cacheInfo.boosterInfo = {};
  2157. cacheInfo.sackItemId = '';
  2158. saveStorage(cacheKey, cacheInfo);
  2159. } else {
  2160. cacheInfo = getStorage(cacheKey);
  2161. }
  2162.  
  2163. let boosterPattern = new RegExp("http[s]?://steamcommunity.com/*tradingcards/boostercreator");
  2164. let appPattern = new RegExp("http[s]?://store.steampowered.com/app/[1-9]*/*");
  2165. let lockAppPattern = new RegExp("http[s]?://store.steampowered.com/agecheck/app/[1-9]*/*");
  2166. let marketPattern = new RegExp("http[s]?://steamcommunity.com/market/*");
  2167. if (boosterPattern.test(window.location.href)) {
  2168. pageLocation = 2;
  2169. initBooster();
  2170. } else if (appPattern.test(window.location.href)) {
  2171. pageLocation = 3;
  2172. currentAppId = window.location.href.match(/app\/(\d+)/)[1];
  2173. currentAppName = document.getElementsByClassName("apphub_AppName")[0].innerHTML
  2174. initApp();
  2175. } else if (lockAppPattern.test(window.location.href)) {
  2176. pageLocation = 4;
  2177. currentAppId = window.location.href.match(/app\/(\d+)/)[1];
  2178. initApp();
  2179. } else if (marketPattern.test(window.location.href)) {
  2180. pageLocation = 5;
  2181. generateSearchMarket();
  2182. }
  2183. }
  2184.  
  2185. //执行初始化工作
  2186. init();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址