您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
小圣杯自动建塔
// ==UserScript== // @name TinyGrail AutoTemple // @namespace https://github.com/bangumi/scripts/tree/master/liaune // @version 0.2 // @description 小圣杯自动建塔 // @author Liaune // @include /^https?://(bgm\.tv|bangumi\.tv|chii\.in)/(user|character|rakuen\/topic\/crt).* // @grant GM_addStyle // ==/UserScript== let api = 'https://tinygrail.com/api/'; function getData(url, callback) { if (!url.startsWith('http')) url = api + url; $.ajax({ url: url, type: 'GET', xhrFields: { withCredentials: true }, success: callback }); } function postData(url, data, callback) { let d = JSON.stringify(data); if (!url.startsWith('http')) url = api + url; $.ajax({ url: url, type: 'POST', contentType: 'application/json', data: d, xhrFields: { withCredentials: true }, success: callback }); } let autoTempleList = JSON.parse(localStorage.getItem('TinyGrail_autoTempleList')) || []; if(autoTempleList.length){ setInterval(function(){ autoTempleList = JSON.parse(localStorage.getItem('TinyGrail_autoTempleList')); autoBuildTemple(autoTempleList); },30*60*1000); } async function retryPromise(callback, n=10) { let error; while(n--) { try { return await new Promise(callback); } catch (err) { error = err; await new Promise(resolve => setTimeout(resolve, 300)); // sleep 300 ms } } throw error; }; async function autoBuildTemple(charas){ closeDialog(); var dialog = `<div id="TB_overlay" class="TB_overlayBG TB_overlayActive"></div> <div id="TB_window" class="dialog" style="display:block;max-width:640px;min-width:400px;"> <div class="info_box"> <div class="title">自动建塔检测中</div> <div class="result" style="max-height:500px;overflow:auto;"></div> </div> <a id="TB_closeWindowButton" title="Close">X关闭</a> </div> </div>`; $('body').append(dialog); $('#TB_closeWindowButton').on('click', closeDialog); $('#TB_overlay').on('click', closeDialog); function buildTemple(chara, index, amount){ postData(`chara/sacrifice/${chara.charaId}/${amount}/false`, null); //if (d.State == 0) { $('.info_box .result').prepend(`<div class="row">#${chara.charaId} ${chara.name} 献祭${amount}</div>`); $('#autobuildButton').text('[自动建塔]'); autoTempleList.splice(index,1); //建塔完成,取消自动建塔 localStorage.setItem('TinyGrail_autoTempleList',JSON.stringify(autoTempleList)); //} else { //$('.info_box .result').prepend(`<div class="row">${d.Message}</div>`); //} } function postBid(chara, price, amount){ postData(`chara/bid/${chara.charaId}/${price}/${amount}`, null, function(d, s) { if(d.Message){ $('.info_box .result').prepend(`<div class="row">#${charaId} ${chara.name} ${d.Message}</div>`); } else{ $('.info_box .result').prepend(`<div class="row">买入成交 #${charaId} ${chara.name} ${price}*${amount}</div>`); } }); } for (let i = 0; i < charas.length; i++) { $('.info_box .title').text(`自动建塔检测中 ${i+1} / ${charas.length}`); let chara = charas[i]; let index = i; $('.info_box .result').prepend(`<div class="row">check #${chara.charaId} ${chara.name}</div>`); await retryPromise(resolve => getData(`chara/user/${chara.charaId}`, function (d, s) { let Amount = d.Value.Amount; if(Amount >= chara.target){ //持股达到数量,建塔 buildTemple(chara, index, chara.target); } else getData(`chara/depth/${chara.charaId}`,function (d, s) { let AskPrice = d.Value.Asks[0] ? d.Value.Asks[0].Price : 0; let AskAmount = d.Value.Asks[0] ? d.Value.Asks[0].Amount : 0; if(AskPrice && AskPrice <= chara.BidPrice){ //最低卖单低于买入上限,买入 postBid(chara, AskPrice, Math.min(AskAmount,chara.target - Amount)); } }); resolve(); if(i == charas.length-1){ $('.info_box .title').text(`自动建塔检测完毕! ${i+1} / ${charas.length}`); setTimeout(()=>{closeDialog();},1*1000); } })); } } function closeDialog() { $('#TB_overlay').remove(); $('#TB_window').remove(); } function openBuildDialog(chara){ autoTempleList = JSON.parse(localStorage.getItem('TinyGrail_autoTempleList')) || []; let target = 500, bidPrice = 10; let intempleList = false, index = 0; for(let i = 0; i < autoTempleList.length; i++){ if(autoTempleList[i].charaId == chara.Id){ target = autoTempleList[i].target; bidPrice = autoTempleList[i].bidPrice; intempleList = true; index = i; } } let dialog = `<div id="TB_overlay" class="TB_overlayBG TB_overlayActive"></div> <div id="TB_window" class="dialog" style="display:block;"> <div class="title" title="目标数量 / 买入价格"> 自动建塔 - #${chara.Id} 「${chara.Name}」 ${target} / ₵${bidPrice}</div> <div class="desc"><p>设置目标数量之前请先检查是否已经献祭建塔,当持股数超过目标数量时将献祭目标数量建塔</p> 输入 目标数量 / 买入价格(不超过此价格的卖单将自动买入)</div> <div class="label"><div class="trade build"> <input class="target" type="number" style="width:150px" title="目标数量" value="${target}"> <input class="bidPrice" type="number" style="width:150px" title="卖出下限" value="${bidPrice}"> <button id="startBuildButton" class="active">自动建塔</button><button id="cancelBuildButton">取消建塔</button></div> <div class="loading" style="display:none"></div> <a id="TB_closeWindowButton" title="Close">X关闭</a> </div>`; $('body').append(dialog); $('#TB_closeWindowButton').on('click', closeDialog); $('#cancelBuildButton').on('click', function(){ if(intempleList){ autoTempleList.splice(index,1); localStorage.setItem('TinyGrail_autoTempleList',JSON.stringify(autoTempleList)); alert(`取消自动建塔${chara.Name}`); location.reload(); } closeDialog(); }); $('#startBuildButton').on('click', function () { let info = {}; info.charaId = chara.Id.toString(); info.name = chara.Name; info.target = $('.trade.build .target').val(); info.bidPrice = $('.trade.build .bidPrice').val(); if(intempleList){ autoTempleList.splice(index,1); autoTempleList.unshift(info); } else autoTempleList.unshift(info); localStorage.setItem('TinyGrail_autoTempleList',JSON.stringify(autoTempleList)); alert(`启动自动建塔#${chara.Id} ${chara.Name}`); closeDialog(); $('#autobuildButton').text('[自动建塔中]'); autoBuildTemple(autoTempleList); }); } function setBuildTemple(charaId){ let charas = []; for(let i = 0; i < autoTempleList.length; i++){ charas.push(autoTempleList[i].charaId); } let button; if(charas.includes(charaId)){ button = `<button id="autobuildButton" class="text_button">[自动建塔中]</button>`; } else{ button = `<button id="autobuildButton" class="text_button">[自动建塔]</button>`; } $('#buildButton').after(button); $('#autobuildButton').on('click', () => { getData(`chara/${charaId}`, (d) => { let chara = d.Value; openBuildDialog(chara); }); }); } function observeChara(mutationList) { if(!$('#grailBox .progress_bar, #grailBox .assets_box').length) { fetched = false; return; } if(fetched) return; if($('#grailBox .assets_box').length) { fetched = true; let charaId = $('#grailBox .title .name a')[0].href.split('/').pop(); setBuildTemple(charaId); } // use '.progress_bar' to detect (and skip) ICO characters else if($('#grailBox .progress_bar').length) { observer.disconnect(); } } let fetched = false; let parentNode=null, observer; if(location.pathname.startsWith('/rakuen/topic/crt')) { parentNode = document.getElementById('subject_info'); observer = new MutationObserver(observeChara); } else if(location.pathname.startsWith('/character')) { parentNode = document.getElementById('columnCrtB') observer = new MutationObserver(observeChara); } if(parentNode) observer.observe(parentNode, {'childList': true, 'subtree': true});