您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
一个通过石之家查询fflogs的小插件
// ==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); })();