挂刀网优化

优化界面。优化表格价格列展示信息,增加数据更新时间;支持配置查询参数首次进入时自动查询;增加挂刀比例计算器;

  1. // ==UserScript==
  2. // @name 挂刀网优化
  3. // @namespace https://gf.qytechs.cn/users/1362311
  4. // @version 1.3.3
  5. // @description 优化界面。优化表格价格列展示信息,增加数据更新时间;支持配置查询参数首次进入时自动查询;增加挂刀比例计算器;
  6. // @author honguangli
  7. // @license MIT
  8. // @match https://www.hangknife.com/
  9. // @match https://hangknife.com/
  10. // @icon https://www.hangknife.com/static/imgs/logo_home_black.png
  11. // @run-at document-body
  12. // @grant GM_addStyle
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. const routerHash = '#/Home'; // 需要开启优化的页面
  19. const searchUrl = 'https://www.hangknife.com/api/steam/queryItemList'; // 查询接口
  20. let state = true; // 是否默认开启优化
  21. const updateDurationMinutes = 30; // 期望更新时间间隔,单位为分钟,当前时间与更新时间差值小于此配置时会标黄色
  22. const closeRealTimeData = true; // 是否关闭实时上架数据
  23.  
  24. // csgo配置
  25. const csgoOption = {
  26. game: 'csgo',
  27. selector: '#pane-csgo .el-table', // 表格选择器
  28. priceColumnIndex: 15, // 价格列下标
  29. search: true, // 是否自定义查询,每次进入页面时执行一次
  30. searchParam: {
  31. orderBy: 'minPrice / steamSafeBuyerPrice,ascending', // 排序方式
  32. minPrice: '1', // 最低价
  33. maxPrice: '', // 最高价
  34. last24Volume: '', // 成交数
  35. platform: ["uupin"], // 交易平台
  36. },
  37. minWidth: 190, // 表格价格列最小宽度
  38. _isInit: false, // 是否已初始化
  39. _isSearch: false, // 是否已触发自定义查询
  40. _width: undefined, // 表格价格列宽度,缓存初始值,用于恢复默认样式
  41. _minWidth: undefined, // 表格价格列最小宽度,缓存初始值,用于恢复默认样式
  42. };
  43.  
  44. // dota2配置
  45. const dota2Option = {
  46. game: 'dota2',
  47. selector: '#pane-dota2 .el-table', // 表格选择器
  48. priceColumnIndex: 9, // 价格列下标
  49. search: true, // 是否自定义查询,每次进入页面时执行一次
  50. searchParam: {
  51. orderBy: 'minPrice / steamSafeBuyerPrice,ascending', // 排序方式
  52. minPrice: '1', // 最低价
  53. maxPrice: '', // 最高价
  54. last24Volume: '', // 成交数
  55. platform: ["buff"], // 交易平台
  56. },
  57. minWidth: 190, // 表格价格列最小宽度
  58. _isInit: false, // 是否已初始化
  59. _isSearch: false, // 是否已触发自定义查询
  60. _width: undefined, // 表格价格列宽度,缓存初始值,用于恢复默认样式
  61. _minWidth: undefined, // 表格价格列最小宽度,缓存初始值,用于恢复默认样式
  62. };
  63.  
  64. // csgo 查询参数
  65. // 交易平台可选项
  66. // ['buff', 'dmarket', 'c5game', 'skinPort', 'igxe', 'uupin', 'v5Item', 'csMoney', 'waxpeer', 'eco', 'bitSkins', 'haloskins']
  67. // 排序方式可选项
  68. // {value: 'minPrice / steamSellerPrice,ascending', label: '最优寄售'}
  69. // {value: 'minPrice / steamBuyerPrice,ascending', label: '最优求购'}
  70. // {value: 'minPrice / steamSafeBuyerPrice,ascending', label: '稳定成交'}
  71. // {value: 'minPrice,ascending', label: '底价升序'}
  72. // {value: 'minPrice / steamSellerPrice,descending', label: '第三方寄售价降序'}
  73. // {value: '(buffBuyOrderPrice - minPrice)/minPrice,descending', label: '低于Buff求购'}
  74. // {value: '(uupinBuyerPrice - minPrice)/minPrice,descending', label: '低于UU求购'}
  75. // {value: '(dmarketOrderPrice - minPrice)/minPrice,descending', label: '低于DMarket求购'}
  76.  
  77. // steam货币配置,此处仅保留美元和人民币
  78. const g_rgCurrencyData = {"USD":{"strCode":"USD","eCurrencyCode":1,"strSymbol":"$","bSymbolIsPrefix":true,"bWholeUnitsOnly":false,"strDecimalSymbol":".","strThousandsSeparator":",","strSymbolAndNumberSeparator":""},"CNY":{"strCode":"CNY","eCurrencyCode":23,"strSymbol":"\u00a5","bSymbolIsPrefix":true,"bWholeUnitsOnly":false,"strDecimalSymbol":".","strThousandsSeparator":",","strSymbolAndNumberSeparator":" "},};
  79. // steam交易配置
  80. const g_rgWalletInfo = {
  81. "wallet_currency": 23, // 货币类型,等同于g_rgCurrencyData.eCurrencyCode,23表示人民币元
  82. "wallet_fee": "1", // 是否计算steam交易费
  83. "wallet_fee_minimum": "1", // steam交易费最低金额,单位分;
  84. "wallet_fee_percent": "0.05", // steam交易费比例,百分比;计算方式:卖家收款金额*比例,保留2位小数向下取整,最低收取wallet_fee_minimum配置金额
  85. "wallet_publisher_fee_percent_default": "0.10", // 游戏交易费比例,百分比;计算方式:卖家收款金额*比例,保留2位小数向下取整,最低收取1分
  86. "wallet_fee_base": "0", // steam额外交易费,单位分;计算完steam交易费后直接追加
  87. };
  88.  
  89. // 隐藏原生的折扣内容
  90. // 通过改变DOM属性实现
  91. GM_addStyle('tbody > tr > td:last-child > div.cell[data-optimize="true"] > div:not([data-type="optimize"]) { display: none; }');
  92.  
  93. // 优化成交数量输入框尺寸
  94. GM_addStyle('.el-tabs .el-tab-pane > div > div > div > div:nth-child(2) > div:nth-child(2) > div:first-child > div:first-child { width: 185px !important; }');
  95. GM_addStyle('.el-tabs .el-tab-pane > div > div > div > div:nth-child(2) > div:nth-child(2) > div:first-child > div:first-child > div:first-child { padding: 0 12px; }');
  96.  
  97. // 监听页面变化
  98. // 匹配路由成功后重新初始化
  99. const observerApp = () => {
  100. const interval = setInterval(()=>{
  101. // 获取app节点
  102. const domApp = document.getElementById('app');
  103. const app = domApp.__vue__;
  104.  
  105. if (app && app._isVue) {
  106. // 初始化配置
  107. initOption();
  108. // 插入优化选项
  109. insertOptimizeCheckBox();
  110. // 插入挂刀比例计算器
  111. insertCalculator();
  112. if (closeRealTimeData) {
  113. closeRealTimeDataWebSocket();
  114. }
  115.  
  116. // 开启监听
  117. const observer = new MutationObserver(() => {
  118. // 初始化配置
  119. initOption();
  120. // 等待DOM刷新
  121. app.$nextTick(() => {
  122. if (location.hash === routerHash) {
  123. // 插入优化选项
  124. insertOptimizeCheckBox();
  125. // 插入挂刀比例计算器
  126. insertCalculator();
  127. if (closeRealTimeData) {
  128. closeRealTimeDataWebSocket();
  129. }
  130. }
  131. });
  132. });
  133. observer.observe(domApp, { childList: true });
  134. clearInterval(interval);
  135. }
  136.  
  137. }, 100);
  138. };
  139.  
  140. // 劫持请求
  141. const listenHttpRequest = () => {
  142. let oldOpen = XMLHttpRequest.prototype.open;
  143. XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
  144. this._url = url;
  145. return oldOpen.call(this, method, url, async, user, password);
  146. };
  147. let oldSend = XMLHttpRequest.prototype.send;
  148. XMLHttpRequest.prototype.send = function () {
  149. // 替换查询参数
  150. if (this._url === searchUrl) {
  151. const args = JSON.parse(arguments[0]);
  152. this._game = args.game;
  153. // 替换自定义查询参数
  154. if (args.game === 'csgo' && !csgoOption._isSearch) {
  155. Object.entries(csgoOption.searchParam).forEach(item => {
  156. if (args.hasOwnProperty(item[0])) {
  157. args[item[0]] = item[1];
  158. }
  159. });
  160. arguments[0] = JSON.stringify(args);
  161. } else if (args.game === 'dota2' && !dota2Option._isSearch) {
  162. Object.entries(dota2Option.searchParam).forEach(item => {
  163. if (args.hasOwnProperty(item[0])) {
  164. args[item[0]] = item[1];
  165. }
  166. });
  167. arguments[0] = JSON.stringify(args);
  168. }
  169. }
  170. // 监听请求响应
  171. this.addEventListener("load", function () {
  172. if (this._url === searchUrl) {
  173. // 同步自定义查询参数
  174. if (this._game === 'csgo' && csgoOption.search && !csgoOption._isSearch) {
  175. csgoOption._isSearch = true;
  176. const tableElement = document.querySelector(csgoOption.selector);
  177. const table = tableElement.__vue__;
  178. Object.entries(csgoOption.searchParam).forEach(item => {
  179. if (table.$parent.initPage.hasOwnProperty(item[0])) {
  180. table.$parent.initPage[item[0]] = item[1];
  181. }
  182. });
  183. } else if (this._game === 'dota2' && dota2Option.search && !dota2Option._isSearch) {
  184. dota2Option._isSearch = true;
  185. const tableElement = document.querySelector(dota2Option.selector);
  186. const table = tableElement.__vue__;
  187. Object.entries(dota2Option.searchParam).forEach(item => {
  188. if (table.$parent.initPage.hasOwnProperty(item[0])) {
  189. table.$parent.initPage[item[0]] = item[1];
  190. }
  191. });
  192. }
  193.  
  194. // 触发优化选项
  195. if (this.readyState == 4 && this.status == 200) {
  196. if (this._game === 'csgo' || this._game === 'dota2') {
  197. changeState(state);
  198. }
  199. }
  200. }
  201. });
  202. return oldSend.apply(this, arguments);
  203. };
  204. };
  205.  
  206. // 初始化配置
  207. const initOption = () => {
  208. // 重置配置
  209. csgoOption._isInit = false;
  210. csgoOption._isSearch = false;
  211. csgoOption._width = undefined;
  212. csgoOption._minWidth = undefined;
  213. dota2Option._isInit = false;
  214. dota2Option._isSearch = false;
  215. dota2Option._width = undefined;
  216. dota2Option._minWidth = undefined;
  217. };
  218.  
  219. // 插入优化选项
  220. const insertOptimizeCheckBox = () => {
  221. if (document.getElementById('optimize-checkbox')) {
  222. return;
  223. }
  224.  
  225. // 获取右侧导航栏
  226. const domRightMenu = document.getElementsByClassName('avatarDiv')[0];
  227.  
  228. // 添加优化选项
  229. const h = `<label id="optimize-checkbox" class="el-checkbox is-bordered" style="height: 32px; margin-right: 8px; padding: 6px 12px;">
  230. <span class="el-checkbox__input">
  231. <span class="el-checkbox__inner"></span>
  232. </span>
  233. <span class="el-checkbox__label">自定义优化</span>
  234. </label>
  235. `;
  236. domRightMenu.insertAdjacentHTML('afterBegin', h);
  237.  
  238. const checkbox = document.getElementById('optimize-checkbox');
  239. checkbox.addEventListener('click', (e) => {
  240. state = !state;
  241. changeState(state);
  242. });
  243.  
  244. // 是否默认开启优化
  245. if (state) {
  246. changeState(state);
  247. }
  248. };
  249.  
  250. // 插入挂刀比例计算器
  251. const insertCalculator = () => {
  252. if (document.getElementById('calculator')) {
  253. return;
  254. }
  255.  
  256. const row = document.querySelector('.el-main');
  257. if (!row) {
  258. return;
  259. }
  260.  
  261. const h = `<div id="calculator" style="display: flex; justify-content: end; align-items: center; column-gap: 12px; margin-bottom: 20px;">
  262. <div class="el-input el-input--small el-input-group el-input-group--prepend" style="width: 100px;">
  263. <div class="el-input-group__prepend">第三方平台购入价</div>
  264. <input id="calculator-buy-price" type="text" autocomplete="off" class="el-input__inner" style="width: 100px;">
  265. </div>
  266. <div class="el-input el-input--small el-input-group el-input-group--prepend" style="width: 100px;">
  267. <div class="el-input-group__prepend">Steam买家支付价</div>
  268. <input id="calculator-sell-price" type="text" autocomplete="off" class="el-input__inner" style="width: 100px;">
  269. </div>
  270. <div class="el-input el-input--small el-input-group el-input-group--prepend is-disabled" style="width: 100px;">
  271. <div class="el-input-group__prepend">Steam到手余额</div>
  272. <input id="calculator-steam-balance" type="text" autocomplete="off" class="el-input__inner" style="width: 100px; color: #67c23a;" disabled>
  273. </div>
  274. <div class="el-input el-input--small el-input-group el-input-group--prepend is-disabled" style="width: 100px;">
  275. <div class="el-input-group__prepend">比例</div>
  276. <input id="calculator-discount" type="text" autocomplete="off" class="el-input__inner" style="width: 100px;" disabled>
  277. </div>
  278. <div>
  279. `;
  280. row.insertAdjacentHTML('afterBegin', h);
  281.  
  282. // 计算折扣
  283. const calculate = (buyPrice, sellPrice) => {
  284. const inputSteamBalance = document.getElementById('calculator-steam-balance');
  285. const inputDiscount = document.getElementById('calculator-discount');
  286.  
  287. // 格式化金额
  288. const buyPriceInt = GetPriceValueAsInt(buyPrice);
  289. const sellPriceInt = GetPriceValueAsInt(sellPrice);
  290.  
  291. // 若steam买家支付金额未设置,则不显示steam到手余额和折扣信息
  292. if (sellPriceInt <= 0) {
  293. inputSteamBalance.value = '';
  294. inputDiscount.value = '';
  295. return;
  296. }
  297.  
  298. const fees = CalculateFeeAmount(sellPriceInt, g_rgWalletInfo.wallet_publisher_fee_percent_default);
  299. inputSteamBalance.value = (fees.amount - fees.fees)/100;
  300.  
  301. // 若第三方平台购入金额未设置,则不显示折扣信息
  302. if (buyPriceInt <= 0) {
  303. inputDiscount.value = '';
  304. return;
  305. }
  306.  
  307. const discount = buyPriceInt / (fees.amount - fees.fees);
  308. inputDiscount.value = discount.toFixed(3);
  309. if (discount <= 0.7) {
  310. inputDiscount.style.setProperty('color', '#67c23a');
  311. } else if (discount <= 0.8) {
  312. inputDiscount.style.setProperty('color', '#409eff');
  313. } else if (discount <= 0.9) {
  314. inputDiscount.style.setProperty('color', '#e6a23c');
  315. } else {
  316. inputDiscount.style.setProperty('color', '#f56c6c');
  317. }
  318. };
  319.  
  320. const inputBuyPrice = document.getElementById('calculator-buy-price');
  321. const inputSellPrice = document.getElementById('calculator-sell-price');
  322.  
  323. inputBuyPrice.addEventListener('input', (e) => {
  324. calculate(inputBuyPrice.value, inputSellPrice.value);
  325. });
  326.  
  327. inputSellPrice.addEventListener('input', (e) => {
  328. calculate(inputBuyPrice.value, inputSellPrice.value);
  329. });
  330. };
  331.  
  332. /**
  333. * 优化状态变更
  334. * @param {bool} state 状态
  335. */
  336. const changeState = (state) => {
  337. if (!state) {
  338. // 关闭
  339. const checkbox = document.getElementById('optimize-checkbox');
  340. checkbox.classList.remove('is-checked');
  341. checkbox.querySelector('.el-checkbox__input').classList.remove('is-checked');
  342. reset(csgoOption);
  343. reset(dota2Option);
  344. } else {
  345. // 开启
  346. const checkbox = document.getElementById('optimize-checkbox');
  347. checkbox.classList.add('is-checked');
  348. checkbox.querySelector('.el-checkbox__input').classList.add('is-checked');
  349. optimize(csgoOption);
  350. optimize(dota2Option);
  351. }
  352. };
  353.  
  354. /**
  355. * 优化表格
  356. * @param {object} option 配置
  357. */
  358. const optimize = (option) => {
  359. const tableElement = document.querySelector(option.selector);
  360. if (!tableElement) {
  361. return;
  362. }
  363.  
  364. const table = tableElement.__vue__;
  365. if (!table) {
  366. return;
  367. }
  368.  
  369. // 初始化
  370. if (!option._isInit) {
  371. option._isInit = true;
  372.  
  373. // 缓存参数
  374. option._width = table.columns[option.priceColumnIndex].width;
  375. option._minWidth = table.columns[option.priceColumnIndex].minWidth;
  376. }
  377. // 执行优化
  378. setTimeout(()=>{
  379. optimizeTable(table, option);
  380. }, 50);
  381. };
  382.  
  383. /**
  384. * 优化表格
  385. * @param {object} table 表格对象
  386. * @param {object} option 配置
  387. */
  388. const optimizeTable = (table, option) => {
  389. // 开启表格纵向边框,开启后可以拖动改变列宽度
  390. table.border = true;
  391. // 设置折扣列宽度,需清除width并设置最小宽度,否则宽度占不满则会出现空白列
  392. table.columns[option.priceColumnIndex].width = undefined;
  393. table.columns[option.priceColumnIndex].minWidth = option.minWidth;
  394. // 修改UI后需重新渲染表格布局
  395. table.doLayout();
  396.  
  397. // 移除优化生成的内容
  398. const domOptimizes = table.$el.querySelectorAll(`tbody > tr > td:nth-child(${ option.priceColumnIndex+1 }) > div.cell > div[data-type="optimize"]`);
  399. for (let i = 0; i < domOptimizes.length; i++) {
  400. domOptimizes[i].parentNode.removeAttribute('data-optimize');
  401. domOptimizes[i].remove();
  402. }
  403.  
  404. // 找到每行的折扣内容
  405. const cells = table.$el.querySelectorAll(`tbody > tr > td:nth-child(${ option.priceColumnIndex+1 }) > div.cell`);
  406.  
  407. const nowUnix = Math.round(new Date() / 1000);
  408.  
  409. // 遍历每行生成优化内容
  410. const data = table.data;
  411. for (let i = 0; i < data.length; i++) {
  412. // 寄售
  413. const sellerFees = CalculateFeeAmount(GetPriceValueAsInt(data[i].steamSellerPrice+''), g_rgWalletInfo.wallet_publisher_fee_percent_default);
  414. const sellerDiscount = (data[i].minPlatformPrice * 100) / (sellerFees.amount - sellerFees.fees);
  415. // 求购
  416. const buyerFees = CalculateFeeAmount(GetPriceValueAsInt(data[i].steamBuyerPrice+''), g_rgWalletInfo.wallet_publisher_fee_percent_default);
  417. const buyerDiscount = (data[i].minPlatformPrice * 100) / (buyerFees.amount - buyerFees.fees);
  418. // 稳定
  419. const safeBuyerFees = CalculateFeeAmount(GetPriceValueAsInt(data[i].steamSafeBuyerPrice+''), g_rgWalletInfo.wallet_publisher_fee_percent_default);
  420. const safeBuyerDiscount = (data[i].minPlatformPrice * 100) / (safeBuyerFees.amount - safeBuyerFees.fees);
  421. const h = `
  422. <div data-type="optimize">
  423. <div style="display: flex;">
  424. <div style="min-width: 33px;">寄售:</div>
  425. ${ data[i].steamSellerPrice === null ? '<div style="flex: 1; color: rgb(170, 170, 170);">暂无记录</div>' : `
  426. <div style="flex: 1">${ table.$parent.formatterPrice(sellerFees.amount/100) } &rarr; ${ table.$parent.formatterPrice((sellerFees.amount-sellerFees.fees)/100) }</div>
  427. <span class="el-tag el-tag--${ table.$parent.formatterTagType(sellerDiscount) } el-tag--mini el-tag--plain">${ sellerDiscount.toFixed(3) }</span>
  428. `}
  429. </div>
  430. <div style="display: flex;">
  431. <div style="min-width: 33px;">求购:</div>
  432. ${ data[i].steamBuyerPrice === null ? '<div style="flex: 1; color: rgb(170, 170, 170);">暂无记录</div>' : `
  433. <div style="flex: 1">${ table.$parent.formatterPrice(buyerFees.amount/100) } &rarr; ${ table.$parent.formatterPrice((buyerFees.amount-buyerFees.fees)/100) }</div>
  434. <span class="el-tag el-tag--${ table.$parent.formatterTagType(buyerDiscount) } el-tag--mini el-tag--plain">${ buyerDiscount.toFixed(3) }</span>
  435. `}
  436. </div>
  437. <div style="display: flex;">
  438. <div style="min-width: 33px;">稳定:</div>
  439. ${ data[i].steamSafeBuyerPrice === null ? '<div style="flex: 1; color: rgb(170, 170, 170);">暂无记录</div>' : `
  440. <div style="flex: 1">${ table.$parent.formatterPrice(safeBuyerFees.amount/100) } &rarr; ${ table.$parent.formatterPrice((safeBuyerFees.amount-safeBuyerFees.fees)/100) }</div>
  441. <span class="el-tag el-tag--${ table.$parent.formatterTagType(safeBuyerDiscount) } el-tag--mini el-tag--plain">${ safeBuyerDiscount.toFixed(3) }</span>
  442. `}
  443. </div>
  444. <div style="display: flex;">
  445. <div style="min-width: 33px;">更新:</div>
  446. <div style="${ nowUnix - Math.round(new Date(data[i].updateTime)/1000) < updateDurationMinutes * 60 ? 'color: #e6a23c' : '' }">${ data[i].updateTime }</div>
  447. </div>
  448. </div>`;
  449. cells[i].insertAdjacentHTML('beforeEnd', h);
  450. cells[i].setAttribute('data-optimize', 'true');
  451. }
  452. };
  453.  
  454. /**
  455. * 重置
  456. * @param {object} option 配置
  457. */
  458. const reset = (option) => {
  459. const tableElement = document.querySelector(option.selector);
  460. if (!tableElement) {
  461. return;
  462. }
  463.  
  464. const table = tableElement.__vue__;
  465. if (!table) {
  466. return;
  467. }
  468.  
  469. // 关闭表格纵向边框
  470. table.border = false;
  471. // 重置列宽度
  472. table.columns[option.priceColumnIndex].width = option._width;
  473. table.columns[option.priceColumnIndex].minWidth = option._minWidth;
  474. // 修改UI后需重新渲染表格布局
  475. table.doLayout();
  476.  
  477. // 移除优化生成的内容
  478. const domOptimizes = table.$el.querySelectorAll(`tbody > tr > td:nth-child(${ option.priceColumnIndex+1 }) > div.cell > div[data-type="optimize"]`);
  479. for (let i = 0; i < domOptimizes.length; i++) {
  480. domOptimizes[i].parentNode.removeAttribute('data-optimize');
  481. domOptimizes[i].remove();
  482. }
  483. };
  484.  
  485. // 关闭实时上架数据websocket
  486. const closeRealTimeDataWebSocket = () => {
  487. const dom = document.querySelector('.el-header .el-card');
  488. if (!dom || !dom.__vue__ || !dom.__vue__._isVue || !dom.__vue__.$parent || !dom.__vue__.$parent._isVue) {
  489. return;
  490. }
  491. dom.__vue__.$parent.disconnect();
  492. };
  493.  
  494. /**
  495. * @typedef {Object} Fees
  496. * @property {number} steam_fee - steam交易费
  497. * @property {number} publisher_fee - 游戏交易费
  498. * @property {number} fees - 总交易费
  499. * @property {number} amount - 买家支付金额
  500. */
  501.  
  502. /**
  503. * 计算交易费用
  504. * steam官网源码,未作改动
  505. * @param {number} amount 买家支付金额
  506. * @param {number} publisherFee 游戏交易费比例
  507. * @return {Fees} 交易费用明细
  508. */
  509. function CalculateFeeAmount( amount, publisherFee ) {
  510. if ( !g_rgWalletInfo['wallet_fee'] ) {
  511. return 0;
  512. }
  513.  
  514. publisherFee = ( typeof publisherFee == 'undefined' ) ? 0 : publisherFee;
  515.  
  516. // Since CalculateFeeAmount has a Math.floor, we could be off a cent or two. Let's check:
  517. var iterations = 0; // shouldn't be needed, but included to be sure nothing unforseen causes us to get stuck
  518. var nEstimatedAmountOfWalletFundsReceivedByOtherParty = parseInt( ( amount - parseInt( g_rgWalletInfo['wallet_fee_base'] ) ) / ( parseFloat( g_rgWalletInfo['wallet_fee_percent'] ) + parseFloat( publisherFee ) + 1 ) );
  519.  
  520. var bEverUndershot = false;
  521. var fees = CalculateAmountToSendForDesiredReceivedAmount( nEstimatedAmountOfWalletFundsReceivedByOtherParty, publisherFee );
  522. while ( fees.amount != amount && iterations < 10 )
  523. {
  524. if ( fees.amount > amount )
  525. {
  526. if ( bEverUndershot )
  527. {
  528. fees = CalculateAmountToSendForDesiredReceivedAmount( nEstimatedAmountOfWalletFundsReceivedByOtherParty - 1, publisherFee );
  529. fees.steam_fee += ( amount - fees.amount );
  530. fees.fees += ( amount - fees.amount );
  531. fees.amount = amount;
  532. break;
  533. }
  534. else
  535. {
  536. nEstimatedAmountOfWalletFundsReceivedByOtherParty--;
  537. }
  538. }
  539. else
  540. {
  541. bEverUndershot = true;
  542. nEstimatedAmountOfWalletFundsReceivedByOtherParty++;
  543. }
  544.  
  545. fees = CalculateAmountToSendForDesiredReceivedAmount( nEstimatedAmountOfWalletFundsReceivedByOtherParty, publisherFee );
  546. iterations++;
  547. }
  548.  
  549. // fees.amount should equal the passed in amount
  550.  
  551. return fees;
  552. }
  553.  
  554. /**
  555. * 计算交易费用
  556. * steam官网源码,未作改动
  557. * @param {number} receivedAmount 卖家收款金额
  558. * @param {number} publisherFee 游戏交易费比例
  559. * @return {Fees} 交易费用明细
  560. */
  561. function CalculateAmountToSendForDesiredReceivedAmount( receivedAmount, publisherFee ){
  562. if ( !g_rgWalletInfo['wallet_fee'] ){
  563. return receivedAmount;
  564. }
  565.  
  566. publisherFee = ( typeof publisherFee == 'undefined' ) ? 0 : publisherFee;
  567.  
  568. var nSteamFee = parseInt( Math.floor( Math.max( receivedAmount * parseFloat( g_rgWalletInfo['wallet_fee_percent'] ), g_rgWalletInfo['wallet_fee_minimum'] ) + parseInt( g_rgWalletInfo['wallet_fee_base'] ) ) );
  569. var nPublisherFee = parseInt( Math.floor( publisherFee > 0 ? Math.max( receivedAmount * publisherFee, 1 ) : 0 ) );
  570. var nAmountToSend = receivedAmount + nSteamFee + nPublisherFee;
  571.  
  572. return {
  573. steam_fee: nSteamFee,
  574. publisher_fee: nPublisherFee,
  575. fees: nSteamFee + nPublisherFee,
  576. amount: parseInt( nAmountToSend )
  577. };
  578. }
  579.  
  580. /**
  581. * 格式化金额
  582. * steam官网源码,未作改动
  583. * @param {string} strAmount 金额,单位元
  584. * @return {number} 金额,单位分
  585. */
  586. function GetPriceValueAsInt( strAmount ) {
  587. var nAmount;
  588. if ( !strAmount )
  589. {
  590. return 0;
  591. }
  592.  
  593. // Users may enter either comma or period for the decimal mark and digit group separators.
  594. strAmount = strAmount.replace( /,/g, '.' );
  595.  
  596. // strip the currency symbol, set .-- to .00
  597. strAmount = strAmount.replace( GetCurrencySymbol( GetCurrencyCode( g_rgWalletInfo['wallet_currency'] ) ), '' ).replace( '.--', '.00');
  598.  
  599. // strip spaces
  600. strAmount = strAmount.replace( / /g, '' );
  601.  
  602. // Remove all but the last period so that entries like "1,147.6" work
  603. if ( strAmount.indexOf( '.' ) != -1 )
  604. {
  605. var splitAmount = strAmount.split( '.' );
  606. var strLastSegment = splitAmount[splitAmount.length-1];
  607.  
  608. if ( !isNaN( strLastSegment ) && strLastSegment.length == 3 && splitAmount[splitAmount.length-2] != '0' )
  609. {
  610. // Looks like the user only entered thousands separators. Remove all commas and periods.
  611. // Ensures an entry like "1,147" is not treated as "1.147"
  612. //
  613. // Users may be surprised to find that "1.147" is treated as "1,147". "1.147" is either an error or the user
  614. // really did mean one thousand one hundred and forty seven since no currencies can be split into more than
  615. // hundredths. If it was an error, the user should notice in the next step of the dialog and can go back and
  616. // correct it. If they happen to not notice, it is better that we list the item at a higher price than
  617. // intended instead of lower than intended (which we would have done if we accepted the 1.147 value as is).
  618. strAmount = splitAmount.join( '' );
  619. }
  620. else
  621. {
  622. strAmount = splitAmount.slice( 0, -1 ).join( '' ) + '.' + strLastSegment;
  623. }
  624. }
  625.  
  626. var flAmount = parseFloat( strAmount ) * 100;
  627. nAmount = Math.floor( isNaN(flAmount) ? 0 : flAmount + 0.000001 ); // round down
  628.  
  629. nAmount = Math.max( nAmount, 0 );
  630. return nAmount;
  631. }
  632.  
  633. /**
  634. * 获取货币符号
  635. * steam官网源码,未作改动
  636. * @param {string} currencyCode 货币代码
  637. * @return {string} 货币符号
  638. */
  639. // Return the symbol to use for a currency
  640. function GetCurrencySymbol( currencyCode ) {
  641. return g_rgCurrencyData[currencyCode] ? g_rgCurrencyData[currencyCode].strSymbol : currencyCode + ' ';
  642. }
  643.  
  644. /**
  645. * 获取货币代码
  646. * steam官网源码,未作改动
  647. * @param {number} currencyId 货币id
  648. * @return {string} 货币代码
  649. */
  650. function GetCurrencyCode( currencyId ) {
  651. for ( var code in g_rgCurrencyData )
  652. {
  653. if ( g_rgCurrencyData[code].eCurrencyCode == currencyId )
  654. return code;
  655. }
  656. return 'Unknown';
  657. }
  658.  
  659. // 监听页面
  660. observerApp();
  661.  
  662. // 劫持请求
  663. listenHttpRequest();
  664. })();

QingJ © 2025

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