// ==UserScript==
// @name 石之家logs成分查询
// @name:en stonesLogsViewer
// @namespace Umi
// @version 1.0
// @description 一个通过石之家查询fflogs的小插件
// @description:en stonesLogsViewer--
// @author Umi
// @match *://ff14risingstones.web.sdo.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=undefined.
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
const delay = 500;
//api token 可以使用自己的token
const apiToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI5YWUxMzk2ZC0xNmVmLTQ4NjEtOGIwZC05ZTc4NDRkM2JjMWMiLCJqdGkiOiJmZmVkMmI4ZTllNjUwNWI0ZTk4ZGNmN2Y0ZGZkZTlmZjg0MjE3YWIxMjE1ZDk1ZjhkNDBmYjIyMjlkNjE0MTQwODYwNjU1YTdmMzlmYjExMiIsImlhdCI6MTcwMjkyMTY1OS43MTIzNjIsIm5iZiI6MTcwMjkyMTY1OS43MTIzNjUsImV4cCI6MTczNDAyNTY1OS43MDMxOTcsInN1YiI6IiIsInNjb3BlcyI6WyJ2aWV3LXVzZXItcHJvZmlsZSIsInZpZXctcHJpdmF0ZS1yZXBvcnRzIl19.TQvn0mwLoEEppHcxuPnXINVLRjrLM0givJ-sltQfHEUFXVJj_r_QLS57ieDAlsjkzOH9ZMlrdBcK0fbMnDa93Jru95-CwNgjl261nYuBYmemAIkNWgVAwsOjXvNm4feoelRHtz8bpbZmCQ1S8FM8d0gwolueZp5t6Kku2Cpb2qaWv_cERh-uO44smPgiER4Z9j33JlfWvkC8brljVALcKjnu7i27R-qD6hBQhicZN7DJVnJkdZGceu9Kaoq0fJO0wr0E8s8BnWQTLJwW3uGSzXrQGUWATxrwALAqIa_2kjKnaC0sXGUnUGQZdckpW6VGHPnfCGslNarviK72n9kfIOlpJVhL-7T360-CgpEVpnFVxfQCTz0ZnRhqxwy9ixZ7OyAY8ZpQTerkGDb2HlZTTCBB-gOdrUmQvDDEfKnGPfuuHSL4TrY6Ndo-Ips8NAUy67nqGaqAUBmUi9MqA-ZL8oqtW3J_Q0HfbTVPk8RvpCelij9zQpC_LSc0kz46vnr47teiEd_4eY8zW6ataCRP4B7epp0aEp6e-UkJjZ05DBgzYwB-aromyrBB6cJPHOX61ywWoEJIYMb8RL6HdGE8vSxhYTrXZfcUBcmkvVZHFHkvmR4MIJ5zjpESr0gq8_gp1ol_FapBmt39Mpuk0GDRUsTCNJAtp6FRcAY8ul4PUJs";
const url = "https://cn.fflogs.com/api/v2/client";
const zones = [
{
"id": 54,
"name": "万魔殿 荒天之狱",
"difficulty": 101
},
{
"id": 49,
"name": "万魔殿 炼净之狱",
"difficulty": 101
},
{
"id": 44,
"name": "万魔殿 边境之狱",
"difficulty": 101
},
{
"id": 53,
"name": "欧米茄绝境验证战",
"difficulty": 100
},
{
"id": 45,
"name": "幻想龙诗绝境战",
"difficulty": 100
},
{
"id": 43,
"name": "绝境战(旧版本)",
"difficulty": 100
}
];
const defaultZone = 54;
(function() {
function ProcessLocationIcon(startingIcon,key)
{
let parentElement = startingIcon.parentElement.parentElement.parentElement;
let usernameElement = FindUsernameElement(parentElement);
//console.log(`获取的用户名: ${usernameElement ? usernameElement.innerText : '未找到'}`);
let server = FindServer(startingIcon);
//console.log(`获取的服务器: ${server}`);
if (usernameElement && server) {
InsertLogsIcon(usernameElement, server, key)
}
}
function FindUsernameElement(parentElement)
{
// 非个人页:第一个能被鼠标点击、仅包含文本的 span 元素
let elements = parentElement.querySelectorAll('span.cursor');
//console.log("找到的元素数量(非个人页): ", elements.length);
for (let element of elements)
{
if (element.childNodes.length == 1 && element.innerText.length >= 1 && element.innerText.length <= 6)
{
return element;
}
}
// 个人页
elements = parentElement.querySelectorAll('span.ft24.ftw');
for (let element of elements)
{
if (element.childNodes.length == 1 && element.innerText.length >= 1 && element.innerText.length <= 6)
{
return element;
}
}
return null;
}
/*
* 结构 1:
* <i>
* <span>
* <span>区</span>
* <span>服</span>
* </span>
* 结构 2:
* <i>
* <span>区 服</span>
* 结构 3:
* <i>
* <span>区</span>
* <span>服</span>
*/
function FindServer(startingIcon)
{
let element = startingIcon.nextElementSibling;
if (element && element.querySelector('span'))
{
element = element.querySelector('span:first-child');
}
let text = element.textContent.trim();
if (text.includes(' '))
{
return text.split(' ')[1];
}
else
{
return element.nextElementSibling ? element.nextElementSibling.textContent : '';
}
}
async function getData(name,server,zoneId){
var difficulty = zones.find((zone) => {
return zone.id == zoneId
}).difficulty
var graphqlQuery = `
{
characterData {
character(name: "${name}", serverRegion: "cn", serverSlug: "${server}") {
zoneRankings(zoneID: ${zoneId}, difficulty: ${difficulty})
}
}
}
`
try {
let response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json charset=UTF-8',
'Authorization': 'Bearer '+ apiToken
},
body: JSON.stringify({ query: graphqlQuery }),
})
return await response.json();
} catch (error) {
console.log('Request Failed', error);
}
}
function getRankColor(rank){
if(rank == '-') return "#666"
if (rank === 100) return "#e5cc80";
if (rank >= 99) return "#e268a8";
if (rank >= 95) return "#ff8000";
if (rank >= 75) return "#a335ee";
if (rank >= 50) return "#0070ff";
if (rank >= 25) return "#1eff00";
else return "#666";
}
function getBossUrl(bossId){
return `https://assets.rpglogs.cn/img/ff/bosses/${bossId}-icon.jpg?v=2`
}
function getJobIconUrl(jobName){
return `https://assets.rpglogs.cn/img/ff/icons/${jobName}.png`
}
function buildBossLogs(boosId,rank,jobName){
var bossDiv = document.createElement('td');
var bossNode = document.createElement('img');
var rankNode = document.createElement('a');
bossNode.setAttribute('src',getBossUrl(boosId));
bossNode.style.width = '24px';
bossNode.style.height = '24px';
rankNode.innerText = rank?Math.round(rank):'-';;
rankNode.style.color = getRankColor(rankNode.innerHTML);
rankNode.style.textAlign = 'center'
rankNode.style.marginLeft = '10px'
bossDiv.style.display = 'flex'
bossDiv.style.justifyContent = 'center'
bossDiv.style.alignItems = 'center'
bossDiv.style.textAlign = 'center'
bossDiv.style.border = '2px solid #333'
bossDiv.append(bossNode);
bossDiv.append(rankNode);
if(jobName){
var jobNode = document.createElement('img');
jobNode.setAttribute('src',getJobIconUrl(jobName));
jobNode.style.width = '24px';
jobNode.style.height = '24px';
jobNode.style.marginLeft = '4px';
jobNode.style.marginRight = '5px';
jobNode.style.border = '1px solid #555555';
bossDiv.append(jobNode);
}
return bossDiv
}
async function updateData(usernameElement,charaKey,server,zoneId){
var data = await getData(usernameElement.innerText,server,zoneId)
console.log(data)
var rankings = data?.data?.characterData?.character?.zoneRankings.rankings
var logsNode = document.getElementById('chara'+charaKey)
logsNode.style.display = 'flex'
console.log(rankings)
logsNode.innerHTML = ''
rankings?.forEach((ranking) => {
var node = buildBossLogs(ranking.encounter.id,ranking.rankPercent,ranking.spec)
logsNode.append(node);
})
}
function InsertLogsIcon(usernameElement, server,key)
{
var viewerDiv = document.createElement('div')
var selectNode = document.createElement('select')
selectNode.setAttribute('id','chara-select'+key)
selectNode.style.backgroundColor = '#000'
selectNode.style.color = 'white'
selectNode.style.border = 'none'
selectNode.style.outline = 'none'
selectNode.addEventListener('change', () => {
console.log(selectNode.options[selectNode.selectedOptions])
updateData(usernameElement,key,server,selectNode.options[selectNode.selectedIndex].value)
})
zones.forEach((zone) => {
var zoneOption = document.createElement('option')
zoneOption.text = zone.name
zoneOption.value = zone.id
selectNode.add(zoneOption)
if(zone.id === defaultZone){
selectNode.selectedIndex = defaultZone
}
})
var logsNode = document.createElement('tr')
logsNode.setAttribute('id','chara'+key)
logsNode.style.borderCollapse = 'collapse'
logsNode.style.borderTop = '2px solid #333'
logsNode.style.borderBottom = '2px solid #333'
logsNode.style.backgroundColor = '#000'
updateData(usernameElement,key,server,defaultZone)
viewerDiv.style.display = 'flex'
viewerDiv.append(selectNode)
viewerDiv.append(logsNode)
var newNode = document.createElement('a');
var username = usernameElement.innerText;
newNode.id = 'ff-icon';
newNode.href = `https://cn.fflogs.com/character/CN/${server}/${username}`;
newNode.innerHTML = `<img src="https://assets.rpglogs.cn/img/ff/favicon.png" height="30px" width="30px">`;
newNode.style.backgroundColor = '#000'
newNode.border = '2px solid #333'
viewerDiv.append(newNode);
console.log(usernameElement.parentElement.parentElement.parentElement.children.length)
if(usernameElement.parentElement.parentElement.parentElement.children.length == 2){
usernameElement.parentElement.parentElement.insertAdjacentElement('afterend', viewerDiv);
}
else{
usernameElement.parentElement.insertAdjacentElement('afterend', viewerDiv);
}
console.log(`FFLogs 图标已添加到:${username}@${server}`);
}
setInterval(function() {
var elements = document.querySelectorAll('.icon-location.dwcolor'); // 搜索服务器前的图标作为定位特征
elements.forEach(function(iconElement,key) {
if (!iconElement.hasAttribute('data-logs-processed'))
{
ProcessLocationIcon(iconElement,key);
iconElement.setAttribute('data-logs-processed', 'true'); // 标记已处理
}
});
}, delay);
})();