TinyGrail Helper

显示角色发行价,显示拍卖情况,把自己的圣殿排到最前面,股息高于低保隐藏签到

当前为 2019-12-05 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         TinyGrail Helper
// @namespace    https://github.com/bangumi/scripts/tree/master/liaune
// @version      1.0
// @description  显示角色发行价,显示拍卖情况,把自己的圣殿排到最前面,股息高于低保隐藏签到
// @author       Liaune,Cedar
// @include     /^https?://(bgm\.tv|bangumi\.tv|chii\.in)/(character|rakuen\/topiclist|rakuen\/home|rakuen\/topic\/crt).*
// @grant        GM_addStyle
// ==/UserScript==
GM_addStyle(`
.assets .my_temple.item .card {
box-shadow: 0px 0px 5px #FFEB3B;
border: 1px solid #FFC107;
}
.assets .my_temple.item .name a {
font-weight: bold;
color: #0084b4;
}
.assets .item .card {
background-size: cover;
width: 90px;
height: 120px;
border-radius: 5px;
box-shadow: 3px 3px 5px #d8d8d8;
border: 1px solid #e0e0e0;
overflow: hidden;
}
.my_auction {
color: #ffa7cc;
margin-right: 5px;
}
.user_auction {
color: #a7e3ff;
margin-right: 5px;
}
`);
const me = $('#new_comment .reply_author a')[0] ? $('#new_comment .reply_author a')[0].innerText : '';
const api = 'https://tinygrail.com/api/';
var lastEven = false;
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
	});
}

function renderCharacterDepth(chara) {
	var depth = `<small class="raise">+${formatNumber(chara.Bids, 0)}</small><small class="fall">-${formatNumber(chara.Asks, 0)}</small><small class="even">${formatNumber(chara.Change, 0)}</small>`
	return depth;
}

function renderCharacterTag(chara, item) {
	var id = chara.Id;
	var flu = '--';
	var tclass = 'even';
	if (chara.Fluctuation > 0) {
		tclass = 'raise';
		flu = `+${formatNumber(chara.Fluctuation * 100, 2)}%`;
	} else if (chara.Fluctuation < 0) {
		tclass = 'fall';
		flu = `${formatNumber(chara.Fluctuation * 100, 2)}%`;
	}

	var tag = `<div class="tag ${tclass}" title="₵${formatNumber(chara.MarketValue, 0)} / ${formatNumber(chara.Total, 0)}">₵${formatNumber(chara.Current, 2)} ${flu}</div>`
	return tag;
}

function listItemClicked() {
	var link = $(this).find('a.avatar').attr('href');
	if (link) {
		if (parent.window.innerWidth < 1200) {
			$(parent.document.body).find("#split #listFrameWrapper").animate({ left: '-450px' });
		}
		window.open(link, 'right');
	}
}

function normalizeAvatar(avatar) {
	if (!avatar) return '//lain.bgm.tv/pic/user/l/icon.jpg';

	if (avatar.startsWith('https://tinygrail.oss-cn-hangzhou.aliyuncs.com/'))
		return avatar + "!w120";

	var a = avatar.replace("http://", "//");
	return a;
}

function caculateICO(ico) {
	var level = 0;
	var price = 10;
	var amount = 10000;
	var total = 0;
	var next = 100000;

	if (ico.Total < 100000 || ico.Users < 10) {
		return { Level: level, Next: next, Price: 0, Amount: 0 };
	}

	level = Math.floor(Math.sqrt(ico.Total / 100000));
	amount = 10000 + (level - 1) * 7500;
	price = ico.Total / amount;
	next = Math.pow(level + 1, 2) * 100000;

	return { Level: level, Next: next, Price: price, Amount: amount };
}

function formatDate(date) {
	var date = new Date(date);
	return date.format('yyyy-MM-dd hh:mm:ss');
}

function formatTime(timeStr) {
	var now = new Date();
	var time = new Date(timeStr) - (new Date().getTimezoneOffset() + 8 * 60) * 60 * 1000;

	var times = (time - now) / 1000;
	var day = 0;
	var hour = 0;
	var minute = 0;
	var second = 0;
	if (times > 0) {
		day = Math.floor(times / (60 * 60 * 24));
		hour = Math.floor(times / (60 * 60)) - Math.floor(times / (60 * 60 * 24)) * 24;
		minute = Math.floor(times / 60) - Math.floor(times / (60 * 60)) * 60;

		if (day > 0) return `剩余${day}天${hour}小时`;
		else if (hour > 1) return `剩余${hour}小时${minute}分钟`;
		else return `即将结束 剩余${minute}分钟`;
		//return '即将结束';
	} else {
		times = Math.abs(times);
		day = Math.floor(times / (60 * 60 * 24));
		hour = Math.floor(times / (60 * 60));
		minute = Math.floor(times / 60);
		second = Math.floor(times);

		if (minute < 1) {
			return `${second}s ago`;
		} else if (minute < 60) {
			return `${minute}m ago`;
		} else if (hour < 24) {
			return `${hour}h ago`;
		}

		if (day > 1000)
			return 'never';

		return `${day}d ago`;
	}
}

function formatNumber(number, decimals, dec_point, thousands_sep) {
	number = (number + '').replace(/[^0-9+-Ee.]/g, '');
	let n = !isFinite(+number) ? 0 : +number,
		prec = !isFinite(+decimals) ? 2 : Math.abs(decimals),
		sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
		dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
		s = '',
		toFixedFix = function (n, prec) {
			let k = Math.pow(10, prec);
			return '' + Math.ceil(n * k) / k;
		};

	s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
	let re = /(-?\d+)(\d{3})/;
	while (re.test(s[0])) {
		s[0] = s[0].replace(re, "$1" + sep + "$2");
	}

	if ((s[1] || '').length < prec) {
		s[1] = s[1] || '';
		s[1] += new Array(prec - s[1].length + 1).join('0');
	}
	return s.join(dec);
}

function closeDialog() {
	$('#TB_overlay').remove();
	$('#TB_window').remove();
}

//###################################################################################################//

let followList;
if(localStorage.getItem('TinyGrail_followList'))
	followList = JSON.parse(localStorage.getItem('TinyGrail_followList'));
else
	followList = {"charas":[],"auctions":[]};

let path = document.location.pathname;
if (path.startsWith('/rakuen/topiclist')){
	loadFollowMenu();
}

function loadFollowMenu() {
	var item = `<li><a href="#" id="followMenu" class="top">关注</a>
<ul>
<li><a href="#" id="followAuction">关注竞拍</a></li>
<li><a href="#" id="followChara">关注角色</a></li>
</ul>
</li>`;
	$('.timelineTabs').append(item);

	$('#followAuction').on('click', function () {
		menuItemClicked(loadFollowAuction);
	});

	$('#followChara').on('click', function () {
		menuItemClicked(loadFollowChara);
	});
}

function loadFollowAuction(){
	var ids = followList.auctions;
	postData('chara/list', ids, function (d, s) {
		if (d.State === 0) {
			loadCharacterList(d.Value,'auction');
			loadUserAuctions(ids);
		}
	});
}

function loadFollowChara(){
	var ids = followList.charas;
	console.log(ids);
	postData('chara/list', ids, function (d, s) {
		if (d.State === 0) {
			loadCharacterList(d.Value,'chara');
		}
	});
}

function menuItemClicked(callback) {
	$('.timelineTabs a').removeClass('focus');
	$('.timelineTabs a').removeClass('top_focus');
	$('#followMenu').addClass('focus');
	if (callback) callback(1);
}

function loadCharacterList(list,type) {
	$('#eden_tpc_list ul .load_more').remove();
	$('#eden_tpc_list ul').html('');
	for (let i = 0; i < list.length; i++) {
		var item = list[list.length-1-i];
		//console.log(item);
		var chara = renderCharacter(item, type, lastEven);
		lastEven = !lastEven;
		$('#eden_tpc_list ul').append(chara);
	}
	$('.cancel_auction').on('click', (e) => {
		if (!confirm('确定取消关注?')) return;
		var id = $(e.target).data('id');
		switch(type){
			case 'auction':
				followList.auctions.splice(followList.auctions.indexOf(id),1);
				break;
			case 'chara':
				followList.charas.splice(followList.charas.indexOf(id),1);
		}
		localStorage.setItem('TinyGrail_followList',JSON.stringify(followList));
		$(`#eden_tpc_list li[data-id=${id}]`).remove();
	});

	$('#eden_tpc_list .item_list').on('click', listItemClicked);
}

function renderCharacter(item,type,even) {
	var line = 'line_odd';
	if (even) line = 'line_even';
	var amount = '';

	if (item.State != 0) {
		amount = `<small title="持有股份 / 固定资产">${formatNumber(item.State, 0)} / ${formatNumber(item.Sacrifices, 0)}</small>`;
	} else {
		amount = `<small title="固定资产">${formatNumber(item.Sacrifices, 0)}</small>`;
	}

	var tag = renderCharacterTag(item);
	var depth = renderCharacterDepth(item);
	var id = item.Id;
	if(item.CharacterId) id = item.CharacterId;
	var time = item.LastOrder;
	var avatar = `<a href="/rakuen/topic/crt/${id}?trade=true" class="avatar l" target="right"><span class="avatarNeue avatarReSize32 ll" style="background-image:url('${normalizeAvatar(item.Icon)}')"></span></a>`;
	var cancel = `<span><small data-id="${item.Id}" class="cancel_auction">[取消关注]</small></span>`;
	var chara;

	var badge = '';
	if (item.Type === 1) badge = `<span class="badge" title="${formatNumber(item.Rate, 1)}倍分红剩余${item.Bonus}期">×${item.Bonus}</span>`;
	if(type=='auction'){
		chara = `<li class="${line} item_list" data-id="${id}">${avatar}<div class="inner">
<a href="/rakuen/topic/crt/${id}?trade=true" class="title avatar l" target="right">${item.Name}${badge}</a> <small class="grey">(+${item.Rate.toFixed(2)})</small>
<div class="row"><small class="time">${formatTime(time)}</small>
${cancel}</div></div>${tag}</li>`
	}
	//ICO
	else if (item.CharacterId) {
		var pre = caculateICO(item);
		//var percent = formatNumber(item.Total / pre.Next * 100, 0);
		chara = `<li class="${line} item_list" data-id="${id}">${avatar}<div class="inner">
<a href="/rakuen/topic/crt/${id}?trade=true" class="title avatar l" target="right">${item.Name}${badge}</a> <small class="grey">(ICO进行中: lv${pre.Level})</small>
<div class="row"><small class="time">${formatTime(item.End)}</small><span><small> ${formatNumber(item.Total, 0)}/100,000 </small></span>
${cancel}</div></div><div class="tags tag lv${pre.Level}">ICO进行中</div></li>`
	}
	else{
		chara = `<li class="${line} item_list" data-id="${id}">${avatar}<div class="inner">
<a href="/rakuen/topic/crt/${id}?trade=true" class="title avatar l" target="right">${item.Name}${badge}</a> <small class="grey">(+${item.Rate.toFixed(2)} / ${formatNumber(item.Total, 0)} / ₵${formatNumber(item.MarketValue, 0)})</small>
<div class="row"><small class="time">${formatTime(time)}</small>${amount}<span title="买入 / 卖出 / 成交">${depth}</span>
${cancel}</div></div>${tag}</li>`
	}
	return chara;
}

function showInitialPrice(charaId){
	getData(`chara/charts/${charaId}/2019-08-08`, function (d, s) {
		if (d.State === 0) {
			let price = d.Value[0].Begin.toFixed(2);
			$('#grailBox .title .text').append(`<span>发行价:${price}</span>`);
		}
	});
}

function splitAmount(amount){
	var splitAmounts = [];
	var len = Math.ceil(amount / 500);
	for(let i=0;i<len;i++){
		var rest = amount - 500;
		if(rest >= 100) splitAmounts.push(500);
		else if(i<len-1) splitAmounts.push(amount - 100);
		else splitAmounts.push(amount);
		amount = amount - splitAmounts[i];
	}
	return splitAmounts;
}

function splitOrder(charaId){
	$('#bidButton').after(`<button id="split_bidButton" class="active bid">买入</button>`);
	$('#bidButton').hide();
	$('#askButton').after(`<button id="split_askButton" class="active ask">卖出</button>`);
	$('#askButton').hide();

	$('#split_bidButton').on('click', function () {
		var price = $('.bid .price').val();
		var amount = $('.bid .amount').val();
		var splitAmounts = splitAmount(amount);
		var i = 0;
		var splitList= setInterval(function(){
			postData(`chara/bid/${charaId}/${price}/${splitAmounts[i]}`, null, function (d, s) {
				if (d.Message) alert(d.Message);
				i++;
				if(i >= splitAmounts.length){
					clearInterval(splitList);
					location.reload();
				}
			});
			i++;
		},200);
	});

	$('#split_askButton').on('click', function () {
		var price = $('.ask .price').val();
		var amount = $('.ask .amount').val();
		var splitAmounts = splitAmount(amount);
		var i = 0;
		var splitList= setInterval(function(){
			postData(`chara/ask/${charaId}/${price}/${splitAmounts[i]}`, null, function (d, s) {
				if (d.Message) alert(d.Message);
				i++;
				if(i >= splitAmounts.length){
					clearInterval(splitList);
					location.reload();
				}
			});
			i++;
		},200);
	});
}

function showOwnTemple(){
	$('#grailBox .assets_box .assets .item').each(function(i,e){
		if(e.querySelector('.name a').innerText == me){
			e.classList.add('my_temple');
			$('#grailBox .assets_box .assets').prepend(e);
		}
	});
}

function showDetailTemple(charaId){
	getData(`chara/temple/${charaId}`, function (d) {
		var templeAll = {1:0,2:0,3:0};
		for (let i = 0; i < d.Value.length; i++) {
			templeAll[d.Value[i].Level]++;
		}
		$('#grailBox .assets_box .bold .sub').before(`<span class="sub"> (${templeAll[3]} + ${templeAll[2]} + ${templeAll[1]})</span>`);
	});
}

function loadUserAuctions(ids) {
	postData('chara/auction/list', ids, (d) => {
		if (d.State == 0) {
			d.Value.forEach((a) => {
				if (a.State != 0) {
					let userAuction = `<span class="user_auction" title="竞拍人数 / 竞拍数量">${formatNumber(a.State, 0)} / ${formatNumber(a.Type, 0)}</span>`;
					$(`.item_list[data-id=${a.CharacterId}] .time`).after(userAuction);
					$(`#auctionHistoryButton`).before(userAuction);
				}
				if (a.Price != 0) {
					let myAuction = `<span class="my_auction" title="出价 / 数量">₵${formatNumber(a.Price, 2)} / ${formatNumber(a.Amount, 0)}</span>`;
					$(`.item_list[data-id=${a.CharacterId}] .time`).after(myAuction);
					$(`#auctionHistoryButton`).before(myAuction);
				}
			});
		}
	});
}

function followChara(charaId){  //关注角色
	let button;
	if(followList.charas.includes(charaId)){
		button = `<button id="followCharaButton" class="text_button">[取消关注]</button>`;
	}
	else{
		button = `<button id="followCharaButton" class="text_button">[关注角色]</button>`;
	}
	$('#grailBox .title .text').append(button);
	$('#followCharaButton').on('click', () => {
		if(followList.charas.includes(charaId)){
			followList.charas.splice(followList.charas.indexOf(charaId),1);
			$('#followCharaButton').text('[关注角色]');
		}
		else{
			followList.charas.push(charaId);
			$('#followCharaButton').text('[取消关注]');
		}
		localStorage.setItem('TinyGrail_followList',JSON.stringify(followList));
	});
}

function followAuctions(charaId){  //关注竞拍情况
	getData(`chara/user/${charaId}/[email protected]/false`, (d) => {
		if (d.State == 0 && d.Value.Amount > 0) {
			let button;
			if(followList.auctions.includes(charaId)){
				button = `<button id="followAuctionButton" class="text_button">[取消关注]</button>`;
			}
			else{
				button = `<button id="followAuctionButton" class="text_button">[关注竞拍]</button>`;
			}
			$('#buildButton').before(button);
			$('#followAuctionButton').on('click', () => {
				if(followList.auctions.includes(charaId)){
					followList.auctions.splice(followList.auctions.indexOf(charaId),1);
					$('#followAuctionButton').text('[关注竞拍]');
				}
				else{
					followList.auctions.push(charaId);
					$('#followAuctionButton').text('[取消关注]');
				}
				localStorage.setItem('TinyGrail_followList',JSON.stringify(followList));
			});
		}
	});
}

function showAuctionHistory(charaId){
	var button = `<button id="auctionHistorys" class="text_button">[往期拍卖]</button>`;
	$('#auctionHistoryButton').after(button);
	$('#auctionHistoryButton').hide();
	$('#auctionHistorys').on('click', () => {
		getData(`chara/${charaId}`, (d => {
			var chara = d.Value;
			var page = 1;
			openHistoryDialog(chara,page);
		}));
	});
}

function openHistoryDialog(chara,page) {
	var dialog = `<div id="TB_overlay" class="TB_overlayBG TB_overlayActive"></div>
<div id="TB_window" class="dialog" style="display:block;max-width:640px;">
<div class="loading"></div>
<a id="TB_closeWindowButton" title="Close">X关闭</a>
</div>`;
	$('body').append(dialog);
	getData(`chara/charts/${chara.Id}/2019-08-08`, function (d, s) {
		var week = parseInt(((new Date())-(new Date('2019/10/05')))/(7*24*3600*1000)+1);
		var week2 = parseInt(((new Date())-(new Date(d.Value[0].Time)))/(7*24*3600*1000)+1);
		week = Math.min(week,week2);
		getData(`chara/auction/list/${chara.Id}/${page}`, (d => {
			$('#TB_window .loading').hide();
			if (d.State == 0 && d.Value.length > 0) {
				var success = 0;
				var total = 0;
				var result = document.createElement('div');
				result.className = "result";
				d.Value.forEach((a) => {
					var state = "even";
					var name = "失败";
					if (a.State == 1) {
						success++;
						total += a.Amount;
						state = "raise";
						name = "成功";
					}
					var record =`
<div class="row"><span class="time">${formatDate(a.Bid)}</span>
<span class="user"><a target="_blank" href="/user/${a.Username}">${a.Nickname}</a></span>
<span class="price">₵${formatNumber(a.Price, 2)} / ${formatNumber(a.Amount, 0)}</span>
<span class="tag ${state}">${name}</span></div>`;
					$(result).append(record);
				});
				var title = `<div class="title">上${page}周拍卖结果 - #${chara.Id} 「${chara.Name}」 ₵${formatNumber(chara.Current, 2)} / ${formatNumber(chara.Total, 0)}</div>
<div class="desc">共有${d.Value.length}人参与拍卖,成功${success}人 / ${total}股</div>`
				$('#TB_window').append(title);
				$('#TB_window').append(result);
			} else {
				var record =`<div class="desc">无拍卖数据</div>`;
				$('#TB_window').append(record);
			}
			var page_inner = document.createElement('div');
			page_inner.className = "page_inner";
			$('#TB_window').append(page_inner);
			if(page > 1) $(page_inner).append(`<a id="nextweek" class="p" style="float: left;margin-bottom: 5px;margin-left: 50px;">后一周</a>`);
			if(page < week) $(page_inner).append(`<a id="lastweek" class="p" style="float: right;margin-bottom: 5px;margin-right: 50px;">前一周</a>`);
			$('#nextweek').on('click', () => {
				page--;
				closeDialog();
				openHistoryDialog(chara,page);
			});
			$('#lastweek').on('click', () => {
				page++;
				closeDialog();
				openHistoryDialog(chara,page);
			});
		}));
	});
	$('#TB_window').css("margin-left", $('#TB_window').width() / -2);
	$('#TB_window').css("margin-top", $('#TB_window').height() / -2);
	$('#TB_closeWindowButton').on('click', closeDialog);
}

function hideBonusButton() {
	if(!$('#bonusButton').length) return;
	getData('event/share/bonus/test', d => {
		if(d.State == 0 && d.Value.Share > 1500*7) $('#bonusButton').hide();
		//else $('#shareBonusButton').hide();
	});
}

function observeBonus(mutationList) {
	if(!$('#grailBox.rakuen_home button.daily_bonus').length) return;
	observer.disconnect();
	hideBonusButton();
}

let fetched = false;
function observeChara(mutationList) {
	if(!$('#grailBox .progress_bar, #grailBox .assets_box').length) {
		fetched = false;
		return;
	}
	if(fetched) return;
	let charaId = document.location.pathname.split('/').pop();
	if($('#grailBox .title').length) followChara(charaId);  //关注角色
	if($('#grailBox .assets_box').length) {
		fetched = true;
		showInitialPrice(charaId);  //显示发行价
		splitOrder(charaId);  //拆单避税
		loadUserAuctions([charaId]);  //显示竞拍情况
		showAuctionHistory(charaId);  //历史拍卖
		followAuctions(charaId);  //关注竞拍情况
		showOwnTemple();  //显示自己的圣殿
		showDetailTemple(charaId); //显示各级圣殿有多少座
	} // use '.progress_bar' to detect (and skip) ICO characters
	else if($('#grailBox .progress_bar').length) {
		observer.disconnect();
	}
}

let parentNode, 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);
} else if (location.pathname.startsWith('/rakuen/home')) {
	parentNode = document.body;
	observer = new MutationObserver(observeBonus);
}

observer.observe(parentNode, {'childList': true, 'subtree': true});