您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhance your OP.GG experience with this powerful match history analyzer! Automatically calculates your total playtime, average game duration, and most played champions. Features include: 🕒 Total and average game time tracking, 🏆 Most played champion statistics, 📊 Average laning scores, and 🔄 Auto-scroll functionality to load your entire match history. Perfect for players who want to track their gaming habits and performance over time!
// ==UserScript== // @name OP.GG Total & Average Game Time Calculator (Robust Selector) // @namespace http://tampermonkey.net/ // @version 0.8 // @description Enhance your OP.GG experience with this powerful match history analyzer! Automatically calculates your total playtime, average game duration, and most played champions. Features include: 🕒 Total and average game time tracking, 🏆 Most played champion statistics, 📊 Average laning scores, and 🔄 Auto-scroll functionality to load your entire match history. Perfect for players who want to track their gaming habits and performance over time! // @author SimronJ // @match https://*.op.gg/lol/summoners* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // Parse time strings like "32m 21s" or "1h 12m 5s" into seconds function parseTimeToSeconds(timeString) { let h=0, m=0, s=0; const parts = timeString.match(/(?:(\d+)h)?\s*(?:(\d+)m)?\s*(?:(\d+)s)?/); if (parts) { h = parseInt(parts[1]||0); m = parseInt(parts[2]||0); s = parseInt(parts[3]||0); } return h*3600 + m*60 + s; } // Format seconds into "Xh Ym Zs" function formatTime(sec) { const h = Math.floor(sec/3600); const m = Math.floor((sec%3600)/60); const s = sec%60; let str = ''; if (h>0) str += h+'h '; if (m>0 || h>0) str += m+'m '; str += s+'s'; return str.trim(); } // Go through each .box-border entry, find time, sum, and count games with time // Also count champion occurrences function calculateStats() { const entries = document.querySelectorAll('.box-border'); let totalTimeInSeconds = 0; let gamesWithTimeCount = 0; const championCounts = {}; const championLaningScores = {}; // Store laning scores per champion let totalLaningScore = 0; let gamesWithLaningScore = 0; entries.forEach(entry => { // Find the champion image and get its alt text const championImg = entry.querySelector('.flex.items-center.gap-1 a img'); if (championImg && championImg.alt) { const championName = championImg.alt; championCounts[championName] = (championCounts[championName] || 0) + 1; // Initialize champion laning scores if not exists if (!championLaningScores[championName]) { championLaningScores[championName] = { total: 0, count: 0 }; } } // Find laning score const laningElement = entry.querySelector('.text-xs.text-gray-500'); if (laningElement) { const laningText = laningElement.textContent; const laningMatch = laningText.match(/Laning (\d+):/); if (laningMatch) { const laningScore = parseInt(laningMatch[1]); totalLaningScore += laningScore; gamesWithLaningScore++; // Add to champion-specific laning score if (championImg && championImg.alt) { const championName = championImg.alt; championLaningScores[championName].total += laningScore; championLaningScores[championName].count++; } } } // Find the one span that looks like a game-timer const spans = entry.querySelectorAll('span'); for (let sp of spans) { const txt = sp.textContent.trim(); if (/^\d+h\s*\d+m\s*\d+s$/.test(txt) || /^\d+m\s*\d+s$/.test(txt)) { totalTimeInSeconds += parseTimeToSeconds(txt); gamesWithTimeCount++; break; } } }); // Find the most frequent champion let mostFrequentChampion = 'N/A'; let maxCount = 0; for (const champion in championCounts) { if (championCounts[champion] > maxCount) { maxCount = championCounts[champion]; mostFrequentChampion = champion; } } // Calculate average laning scores const averageLaningScore = gamesWithLaningScore > 0 ? Math.round(totalLaningScore / gamesWithLaningScore) : 0; const mostPlayedChampionLaningScore = mostFrequentChampion !== 'N/A' && championLaningScores[mostFrequentChampion] ? Math.round(championLaningScores[mostFrequentChampion].total / championLaningScores[mostFrequentChampion].count) : 0; return { totalSeconds: totalTimeInSeconds, gamesWithTimeCount: gamesWithTimeCount, mostFrequentChampion: mostFrequentChampion, mostFrequentChampionGames: maxCount, averageLaningScore: averageLaningScore, mostPlayedChampionLaningScore: mostPlayedChampionLaningScore }; } // Create or update floating widget function updateDisplay() { const { totalSeconds, gamesWithTimeCount, mostFrequentChampion, mostFrequentChampionGames, averageLaningScore, mostPlayedChampionLaningScore } = calculateStats(); const avgSeconds = gamesWithTimeCount > 0 ? Math.floor(totalSeconds / gamesWithTimeCount) : 0; const totalStr = formatTime(totalSeconds); const avgStr = formatTime(avgSeconds); let el = document.getElementById('time-stats-display'); if (!el) { el = document.createElement('div'); el.id = 'time-stats-display'; Object.assign(el.style, { position: 'fixed', top: '50%', left: '10px', transform: 'translateY(-50%)', backgroundColor:'rgba(0,0,0,0.7)', color: '#fff', padding: '10px', borderRadius: '5px', zIndex: '10000', fontFamily: 'sans-serif', whiteSpace: 'pre-line' }); document.body.appendChild(el); } el.textContent = `Total Played Time: ${totalStr}\n` + `Average per Game: ${avgStr} (${gamesWithTimeCount} games)\n` + `Most Played Champion: ${mostFrequentChampion} (${mostFrequentChampionGames} games | ${mostPlayedChampionLaningScore} Avg Lane)\n` + `Average Laning Score: ${averageLaningScore}`; // Add scroll button if it doesn't exist let scrollButton = document.getElementById('auto-scroll-button'); if (!scrollButton) { scrollButton = document.createElement('button'); scrollButton.id = 'auto-scroll-button'; scrollButton.textContent = 'Auto Scroll'; Object.assign(scrollButton.style, { display: 'block', marginTop: '10px', padding: '5px 10px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '3px', cursor: 'pointer', width: '100%' }); scrollButton.addEventListener('click', autoScroll); el.appendChild(scrollButton); } } // Function to handle auto-scrolling function autoScroll() { const button = document.getElementById('auto-scroll-button'); button.textContent = 'Loading...'; button.disabled = true; let lastHeight = document.body.scrollHeight; let noChangeCount = 0; const loadMore = () => { const showMoreButton = document.querySelector('button.text-13px.border-1.box-border.block.h-\\[40px\\].w-full.border.border-gray-250.bg-gray-0.px-0.py-\\[8px\\].text-center.text-gray-900.no-underline.md\\:rounded'); if (showMoreButton) { showMoreButton.click(); // Check if the page height changed after clicking setTimeout(() => { const newHeight = document.body.scrollHeight; if (newHeight === lastHeight) { noChangeCount++; if (noChangeCount >= 3) { // If height hasn't changed for 3 attempts clearInterval(loadInterval); button.textContent = 'Auto Scroll'; button.disabled = false; } } else { noChangeCount = 0; lastHeight = newHeight; } }, 1000); } else { clearInterval(loadInterval); button.textContent = 'Auto Scroll'; button.disabled = false; } }; const loadInterval = setInterval(loadMore, 1500); // Try to load more every 1.5 seconds } // Watch for newly loaded games (infinite scroll) const observer = new MutationObserver(muts => { for (let m of muts) { if (m.addedNodes.length) { updateDisplay(); break; } } }); const container = document.querySelector('#content-container') || document.body; observer.observe(container, { childList: true, subtree: true }); // Initial render updateDisplay(); })();