// ==UserScript==
// @name 饰品比例列表计算
// @namespace sourcewater
// @version 0.1
// @description 饰品比例列表计算查看
// @author sourcewater
// @match https://buff.163.com/market/?game=*
// @match https://www.c5game.com/dota.html*
// @match https://www.c5game.com/csgo/default/*
// @match https://www.igxe.cn/dota2/*
// @match https://www.igxe.cn/csgo/*
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
let log={
DEBUG:1,
INFO:5,
level:1,
setLevel:function(level){
this.level=level;
},
log:function(msg,level,prefix){
let date=new Date();
let time=date.getHours()+":"+date.getMinutes()+":"+date.getSeconds();
if(level>=this.level){
console.log(prefix+time+" >>>> "+msg);
}
},
info:function (msg){
this.log(msg,this.INFO,"info: ");
},
debug:function(msg){
this.log(msg,this.DEBUG,"debug: ");
}
};
log.setLevel(log.INFO);
let priceCSSEle=document.createElement("style");
priceCSSEle.setAttribute("type","text/css");
const displaySuccessClass="s_s_s_s_display_success";
const itemidAttr="s_s_s_s_item_id";
const successAttr="s_s_s_s_success";
const linkRegAttr="s_s_s_s_click_register";
const contentChangeAttr="s_s_s_s_content_change";
const progressDivId="s_s_s_s_progress_div";
const progressDivAttr="s_s_s_s_progress_init_success";
const waitingDivId="s_s_s_s_waiting_div";
const appidList={"csgo":730,"dota2":570};
const buffUrlReg=/buff\.163\.com\//;
const c5UrlReg=/c5game\.com\//;
const igxeUrlReg=/igxe\.cn/;
const timeout=6000;
let index=0;
let progressDivEle=document.createElement("div");
progressDivEle.setAttribute("id",progressDivId);
progressDivEle.setAttribute("style","border-radius:4px; border: 4px solid white; text-align: center;position: absolute;left: 50%;top: 55%;transform: translate(-50%, -50%);background:white;padding:5px;");
progressDivEle.innerHTML=`
<p>
<strong>正在初始化</strong><br>
<progress style="width:200px;height:25px;"></progress>
</p>
`;
document.body.appendChild(progressDivEle);
let priceCSS=`
.s_s_s_s_display_success{
display:block;
}
.s_s_s_s_cell_right,
.s_s_s_s_cell_link,
.s_s_s_s_cell_after_fee,
.s_s_s_s_cell_rate,
.s_s_s_s_cell_rate_high{
width:50px;
text-align:right;
}
.s_s_s_s_cell_left{
display:inline-block;
width:50px;
text-align:left;
}
.s_s_s_s_cell_right{
color:#eea20e;
}
.s_s_s_s_cell_link{
color:black;
}
.s_s_s_s_cell_after_fee{
color:purple;
}
.s_s_s_s_cell_rate{
color:green;
}
.s_s_s_s_cell_rate_high{
color:red;
}
`;
let customPriceCss;
if(buffUrlReg.test(window.location.href)){
customPriceCss=`
.s_s_s_s_cell_left{
color:black;
}
.s_s_s_s_display_success{
margin-left:15px;
height:167px;
}
`;
}else if(c5UrlReg.test(window.location.href)){
customPriceCss=`
.s_s_s_s_cell_left{
color:white;
}
.s_s_s_s_display_success{
margin-left:15px;
height:205px;
}
`;
}else if(igxeUrlReg.test(window.location.href)){
///////
customPriceCss=`
.s_s_s_s_cell_left{
color:white;
}
.s_s_s_s_display_success{
margin-left:15px;
height:205px;
}
`;
}
priceCSSEle.innerHTML=priceCSS+customPriceCss;
document.head.appendChild(priceCSSEle);
function checkInitCalculate(){
if(!progressDivEle.getAttribute(progressDivAttr)){
initCalculate();
return;
}
log.info("init successfully!");
}
function initCalculate(){
setTimeout(function(){checkInitCalculate();},timeout);
let steamMarketUrl="https://steamcommunity.com/market/";
let walletVariable="var g_rgWalletInfo = ";
GM_xmlhttpRequest({
url: steamMarketUrl,
method: 'GET',
//timeout:10000,
onload: function(res){
if(res.status === 200){
let html=res.responseText;
walletVariable+=html.match(/var g_rgWalletInfo(.|\n)*?(\{+?(.|\n)*?\}+?)+/)[2];
let varScriptEle=document.createElement("script");
varScriptEle.setAttribute("type","text/javascript");
varScriptEle.innerHTML=walletVariable;
document.body.appendChild(varScriptEle);
let calculateJSUrl="https://steamcommunity-a.akamaihd.net/public/javascript/economy_common.js?v=tsXdRVB0yEaR&l=schinese&_cdn=china_pinyuncloud";
GM_xmlhttpRequest({
url: calculateJSUrl,
method: 'GET',
//timeout:10000,
onload: function(res){
if(res.status === 200){
let calculateScript=res.responseText;
let calScriptEle=document.createElement("script");
calScriptEle.setAttribute("type","text/javascript");
calScriptEle.innerHTML=calculateScript;
document.head.appendChild(calScriptEle);
progressDivEle.setAttribute(progressDivAttr,"success");
start(window.location.href);
document.body.removeChild(progressDivEle);
}else{
log.info("initCalculate(): 访问Steam市场错误!");
}
},onerror : function(err){
log.info("initCalculate(): 访问Steam市场超时!");
},ontimeout : function(err){
log.info("initCalculate(): 访问超时");
}
});
}else{
log.info("initCalculate(): 访问Steam市场错误!");
}
},onerror : function(err){
log.info("initCalculate(): 访问Steam市场超时!");
},ontimeout : function(err){
log.info("initCalculate(): 访问超时");
}
});
}
initCalculate();
function getPriceValueAsInt(value){
if(isNaN(value)){
value=parseFloat(value);
}
return (value*100).toString();
}
function receivePirce(price){
if(typeof price != 'undefined'){
return (getPriceValueAsInt(price)-CalculateFeeAmount(getPriceValueAsInt(price),g_rgWalletInfo["wallet_publisher_fee_percent_default"]).fees)/100;
}
return 0;
}
function queryPrice(currentIndex,appid,name,updateView,args){
log.info("new task index: "+currentIndex);
if(currentIndex<index){
log.debug("stop previous check update for index: "+currentIndex);
return;
}
function checkUpdate(currentIndex,item_nameid,steamUrl,element){
if(currentIndex<index){
log.debug("stop previous check update for index: "+currentIndex);
return;
}
if(!args.element.getAttribute(successAttr)){
update(item_nameid,steamUrl);
log.debug("retry get price >>>> "+item_nameid);
}
log.debug(item_nameid+" >>>> get price finish!");
}
function update(item_nameid,steamUrl){
if(currentIndex<index){
log.debug("stop previous check update for index: "+currentIndex);
return;
}
let buyPrice=-1;
let sellPrice=-1;
setTimeout(function(){checkUpdate(currentIndex,item_nameid,steamUrl,args.element);},timeout);
log.debug(`update: "https://steamcommunity.com/market/itemordershistogram?country=CN&language=schinese¤cy=23&item_nameid=${item_nameid}&two_factor=0"`);
GM_xmlhttpRequest({
url: `https://steamcommunity.com/market/itemordershistogram?country=CN&language=schinese¤cy=23&item_nameid=${item_nameid}&two_factor=0`,
method: 'GET',
//timeout:10000,
responseType: 'json',
onload: function(res){
if(res.status === 200){
let data=JSON.parse(res.responseText);
if(data.success==1){
if(data.buy_order_graph.length>0&&data.buy_order_graph[0].length>0){
buyPrice=data.buy_order_graph[0][0];
}
if(data.sell_order_graph.length>0&&data.sell_order_graph[0].length>0){
sellPrice=data.sell_order_graph[0][0];
}
args.sellPrice=sellPrice;
args.buyPrice=buyPrice;
args.steamUrl=steamUrl;
updateView(args);
}
}else{
log.info("update(): "+res.status+" :访问Steam市场错误!");
}
},onerror : function(err){
log.info("update(): 访问Steam市场超时!");
},ontimeout : function(err){
log.info("update(): 访问超时");
}
});
}
function checkFetchInfo(currentIndex,item_nameid){
if(currentIndex<index){
log.debug("stop previous check fetch info for index: "+currentIndex);
return;
}
if(!args.element.getAttribute(itemidAttr)){
fetchInfo();
log.debug("retry get item name id.");
}
log.debug(item_nameid+" >>>> get item name id finish!");
}
function fetchInfo(){
let item_nameid="";
if(currentIndex<index){
log.debug("stop previous check update for index: "+currentIndex);
return;
}
let steamUrl=`https://steamcommunity.com/market/listings/${appid}/${name}`;
setTimeout(function(){checkFetchInfo(currentIndex,item_nameid);},timeout);
GM_xmlhttpRequest({
method: "get",
url: steamUrl,
//timeout:10000,
async: false,
onload: function(res){
if(res.status === 200){
let response=res.responseText;
let nameidMatchs=response.match(/Market_LoadOrderSpread\(.*?([\d]+).*?\);/);
log.debug("<nameidMatchs: "+nameidMatchs+">");
if(nameidMatchs){
item_nameid=nameidMatchs.length == 2 ? nameidMatchs[1] : "";
log.debug("<item_nameid: "+item_nameid+">");
if(item_nameid!=""){
args.element.setAttribute(itemidAttr,"success");
update(item_nameid,steamUrl);
}
}else{
args.element.querySelector("#"+waitingDivId).innerHTML=`<span style="color: red;display:block;text-align: center;position: relative;left: 50%;top: 50%;transform: translate(-50%, -50%);">此物品不在货架上。</span>`;
args.element.setAttribute(itemidAttr,"success");
}
}else{
log.info("Error Code: "+res.status);
}
},onerror : function(err){
log.info("fetchInfo(): 访问Steam市场错误!");
},ontimeout : function(err){
log.info("fetchInfo(): 访问超时");
}
});
}
fetchInfo();
}
function updateView(args){
let purchasePrice,sellRate,buyRate;
let priceSpan=document.createElement("span");
let success=true;
if(buffUrlReg.test(args.url)&&!args.element.getAttribute(successAttr)){
purchasePrice=parseFloat(args.element.querySelector("strong.f_Strong").innerHTML.match(/[\d\.]/g).join(""));
}else if(c5UrlReg.test(args.url)&&!args.element.getAttribute(successAttr)){
purchasePrice=parseFloat(args.element.querySelector("span.price").innerHTML.match(/[\d\.]/g).join(""));
}else if(igxeUrlReg.test(args.url)&&!args.element.getAttribute(successAttr)){
purchasePrice=parseFloat(args.element.querySelector(".price.fl").textContent.match(/[\d\.]+/g).join(""));
}else{
success=false;
}
if(success){
sellRate=(purchasePrice/receivePirce(args.sellPrice)).toFixed(2);
buyRate=(purchasePrice/receivePirce(args.buyPrice)).toFixed(2);
args.element.removeChild(args.element.querySelector("#"+waitingDivId));
priceSpan.setAttribute("class",displaySuccessClass);
let priceContent=`
<br><span class="s_s_s_s_cell_left">卖出:</span><span class="s_s_s_s_cell_right">${(args.sellPrice == -1 ? "无" : args.sellPrice)}</span>
<br><span class="s_s_s_s_cell_left">税后:</span><span class="s_s_s_s_cell_after_fee">${args.sellPrice == -1 ? "无" : receivePirce(args.sellPrice)}</span>
<br><span class="s_s_s_s_cell_left">比例:</span><span class="${ sellRate > 0.7 ? "s_s_s_s_cell_rate_high": "s_s_s_s_cell_rate"}">${args.sellPrice == -1 ? "无" : sellRate}</span>
<br><br><span class="s_s_s_s_cell_left">买入:</span><span class="s_s_s_s_cell_right">${(args.buyPrice == -1 ? "无" : args.buyPrice)}</span>
<br><span class="s_s_s_s_cell_left">税后:</span><span class="s_s_s_s_cell_after_fee">${args.buyPrice == -1 ? "无" : receivePirce(args.buyPrice)}</span>
<br><span class="s_s_s_s_cell_left">比例:</span><span class="${ buyRate > 0.9 ? "s_s_s_s_cell_rate_high": "s_s_s_s_cell_rate"}">${args.buyPrice == -1 ? "无" : buyRate}</span>
<br><br>
<span class="s_s_s_s_cell_link"><a href="${args.steamUrl}" target="_blank">Steam链接</a></span>
`;
priceSpan.innerHTML=priceContent;
args.element.setAttribute(successAttr,"true");
args.element.appendChild(priceSpan);
log.debug("update view successfully!");
}
}
function waitForElement(selector,task){
if(document.querySelector(selector)){
task();
}else{
setTimeout(function(){waitForElement(selector,task);},100);
}
}
let missionStart=false;
function start(url){
let cards;
let cardClass;
let appid;
if(buffUrlReg.test(url)){
let buffCSGOReg=/game=csgo/;
let buffDotaReg=/game=dota2/;
if(buffCSGOReg.test(url)){
cardClass=".card_csgo";
appid=appidList.csgo;
}else if(buffDotaReg.test(url)){
cardClass=".card_dota2";
appid=appidList.dota2;
}
waitForElement(cardClass,function(){
cards=document.querySelector(cardClass).querySelectorAll("li");
startQuery();
});
}else if(c5UrlReg.test(url)){
let c5CSGOReg=/\/csgo\//;
let c5DotaReg=/dota\.html/;
cardClass=".list-item4";
if(c5DotaReg.test(url)){
appid=appidList.dota2;
}else if(c5CSGOReg.test(url)){
appid=appidList.csgo;
}
waitForElement(cardClass,function(){
cards=document.querySelector(cardClass).querySelectorAll("li");
startQuery();
});
}else if(igxeUrlReg.test(url)){
let igxeCSGOReg=/\/csgo\//;
let igxeDotaReg=/\/dota2\//;
cardClass=".dataList";
if(igxeDotaReg.test(url)){
appid=appidList.dota2;
}else if(igxeCSGOReg.test(url)){
appid=appidList.csgo;
}
waitForElement(cardClass,function(){
cards=document.querySelector(cardClass).querySelectorAll("a");
startQuery();
});
}
function startQuery(){
++index;
if(buffUrlReg.test(url)){
for(let i=0;i<cards.length;++i){
let progressEleDiv=document.createElement("div");
progressEleDiv.setAttribute("id",waitingDivId);
queryPrice(index,appid,cards[i].querySelector("a").title,updateView,{"url":url,"element":cards[i]});
cards[i].setAttribute("style","height:399px!important;");
progressEleDiv.setAttribute("style","width:208px;height:120px;");
progressEleDiv.innerHTML=`<progress style="text-align: center;position: relative;left: 50%;top: 50%;transform: translate(-50%, -50%);"></progress>`;
cards[i].appendChild(progressEleDiv);
}
let marketCard=document.querySelector("#j_market_card");
if(!missionStart){
let marketCardConfig = { childList: true };
let observer = new MutationObserver(function(){
if(!document.querySelector(cardClass)){
if(!marketCard.getAttribute(contentChangeAttr)){
marketCard.setAttribute(contentChangeAttr,"true");
}
}
});
observer.observe(marketCard, marketCardConfig);
let marketCardConfigAttr={ attributes: true };
let observerAttr = new MutationObserver(function(){
if(marketCard.getAttribute(contentChangeAttr)){
marketCard.removeAttribute(contentChangeAttr);
start(window.location.href);
}
});
observerAttr.observe(marketCard, marketCardConfigAttr);
missionStart=true;
}
}else if(c5UrlReg.test(url)){
for(let i=0;i<cards.length;++i){
let progressEleSpan=document.createElement("span");
progressEleSpan.setAttribute("style","height:205px;display:block;");
progressEleSpan.setAttribute("id",waitingDivId);
queryPrice(index,appid,cards[i].querySelector("img").alt,updateView,{"url":url,"element":cards[i]});
progressEleSpan.innerHTML=`<progress style="text-align: center;position: relative;left: 50%;top: 50%;transform: translate(-50%, -50%);"></progress>`;
cards[i].appendChild(progressEleSpan);
}
}else if(igxeUrlReg.test(url)){
const itemEnNameAttr="s_s_s_s_item_enName_success";
function checkGetEnNameAndQuery(appid,cnName,progressEleSpan,card){
if(!card.getAttribute(itemEnNameAttr)){
getEnNameAndQuery(appid,cnName,progressEleSpan,card);
log.debug("retry get en name");
}
log.debug("get en name for "+cnName+" successfully!");
}
function getEnNameAndQuery(appid,cnName,progressEleSpan,card){
setTimeout(function(){checkGetEnNameAndQuery(appid,cnName,progressEleSpan,card);},timeout);
GM_xmlhttpRequest({
url: `https://steamcommunity.com/market/search?appid=${appid}&q=${cnName}`,
method: 'GET',
onload: function(res){
if(res.status === 200){
let html=res.responseText;
let itemLinkListReg=/<a[\s]+?class="market_listing_row_link"[\s\S]*?<\/a>/g;
let itemNameSpanReg=/<span[\s\S]+?class="market_listing_item_name"[\s\S]*?>(.+?)<\/span>/;
let enNameReg=/<div[\s\S]+?class="market_listing_row market_recent_listing_row market_listing_searchresult"[\s\S]*?data-hash-name="(.+)"[\s\S]*?>/
let enName="";
let linkList=html.match(itemLinkListReg);
if(linkList){
for(let i=0;i<linkList.length;++i){
let matchCNName=linkList[i].match(itemNameSpanReg)[1];
if(matchCNName==cnName){
enName=linkList[i].match(enNameReg)[1];
break;
}
}
if(enName==""){
progressEleSpan.innerHTML=`<span style="color: red;display:block;text-align: center;position: relative;left: 50%;top: 50%;transform: translate(-50%, -50%);">此物品不在货架上。</span>`;
card.setAttribute(itemidAttr,"success");
}else{
queryPrice(index,appid,enName,updateView,{"url":url,"element":card});
}
card.setAttribute(itemEnNameAttr,"success");
}
}else{
log.info("startQuery(): 访问Steam市场错误!Code: "+res.status);
}
},onerror : function(err){
log.info("startQuery(): 访问Steam市场超时!");
},ontimeout : function(err){
log.info("startQuery(): 访问超时");
}
});
}
for(let i=0;i<cards.length;++i){
let progressEleSpan=document.createElement("span");
progressEleSpan.setAttribute("style","height:205px;display:block;");
progressEleSpan.setAttribute("id",waitingDivId);
let card=cards[i];
let cnName=card.querySelector(".name").title;
card.setAttribute("style","height: 450px;");
progressEleSpan.innerHTML=`<progress style="text-align: center;position: relative;left: 50%;top: 50%;transform: translate(-50%, -50%);"></progress>`;
card.appendChild(progressEleSpan);
getEnNameAndQuery(appid,cnName,progressEleSpan,card)
}
}
}
}
})();