Session Info

Race data

目前为 2024-10-28 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/514399/1472771/Session%20Info.js

  1. // ==UserScript==
  2. // @name Session Info
  3. // @namespace https://greasyfork.org/users/1331131-tensorflow-dvorak
  4. // @version 2024-10-26
  5. // @description Race data
  6. // @match *://*.nitrotype.com/race
  7. // @match *://*.nitrotype.com/race/*
  8. // @require https://update.greasyfork.org/scripts/501960/1418069/findReact.js
  9. // @license MIT
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. let sessionStartTime;
  17. let raceTimes = [];
  18. let averageWPM = 0;
  19.  
  20. function initializeSessionData() {
  21. const persistData = localStorage.getItem("persist:nt");
  22. if (persistData) {
  23. const parsedData = JSON.parse(JSON.parse(persistData).user);
  24. sessionStartTime = parseInt(localStorage.getItem("sessionStartTime")) || Date.now();
  25. raceTimes = JSON.parse(localStorage.getItem("raceTimes")) || [];
  26. averageWPM = parsedData.avgSpeed || 0;
  27.  
  28. if (!localStorage.getItem("sessionStartTime")) {
  29. localStorage.setItem("sessionStartTime", sessionStartTime);
  30. }
  31.  
  32. return parsedData.sessionRaces || 0;
  33. }
  34. return 0;
  35. }
  36.  
  37. let sessionRaces = initializeSessionData();
  38.  
  39. function createPostRaceInfo() {
  40. const dashElement = document.querySelector(".dash");
  41. const settingsElement = document.querySelector(
  42. ".nitro-monkey__settings-container"
  43. );
  44.  
  45. if (dashElement && settingsElement) {
  46. const postRaceContainer = document.createElement("div");
  47. postRaceContainer.classList.add("nitro-monkey__pr-container");
  48.  
  49. postRaceContainer.style.display = "flex";
  50. postRaceContainer.style.justifyContent = "center";
  51. postRaceContainer.style.alignItems = "center";
  52. postRaceContainer.style.padding = "0.5rem";
  53. postRaceContainer.style.backgroundColor = "#1a1a2e";
  54. postRaceContainer.style.borderRadius = "8px";
  55. postRaceContainer.style.boxShadow = "0 2px 8px rgba(0, 0, 0, 0.15)";
  56. postRaceContainer.style.width = "auto";
  57. postRaceContainer.style.height = "fit-content";
  58. postRaceContainer.style.color = "#e0e0e0";
  59. postRaceContainer.style.fontSize = "0.9rem";
  60. postRaceContainer.style.marginBottom = "1rem";
  61.  
  62. postRaceContainer.innerHTML = `
  63. <table style="width: auto; border-collapse: collapse; font-size: 0.9rem;">
  64. <tbody>
  65. <tr>
  66. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Session Races:</td>
  67. <td id="sessionValue" style="padding: 0.4rem; text-align: right;">${sessionRaces}</td>
  68. </tr>
  69. <tr>
  70. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Races/hr:</td>
  71. <td id="hourlyRaces" style="padding: 0.4rem; text-align: right;">${calculateRacesLastHour()}</td>
  72. </tr>
  73. <tr>
  74. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Est. Races/hr:</td>
  75. <td id="estimatedRaces" style="padding: 0.4rem; text-align: right;">${localStorage.getItem("estimatedRaces") || 0}</td>
  76. </tr>
  77. <tr>
  78. <td style="padding: 0.4rem; text-align: left; color: #a0a0a0;">Average WPM:</td>
  79. <td id="averageWPM" style="padding: 0.4rem; text-align: right;">${averageWPM}</td>
  80. </tr>
  81. <tr>
  82. <td colspan="2" style="text-align: center; padding: 0.5rem;">
  83. <button id="resetButton" style="padding: 0.3rem 0.8rem; font-size: 0.85rem; border: none; border-radius: 5px; background-color: #4ecca3; color: #1a1a2e; cursor: pointer;">Reset</button>
  84. </td>
  85. </tr>
  86. </tbody>
  87. </table>
  88. `;
  89.  
  90. settingsElement.insertBefore(postRaceContainer, settingsElement.firstChild);
  91.  
  92. document.getElementById("resetButton").addEventListener("click", resetSessionData);
  93.  
  94. return true;
  95. }
  96. return false;
  97. }
  98.  
  99. const getTypedCharacterCount = () => {
  100. return document.querySelectorAll(".dash-letter.is-correct.is-typed")?.length;
  101. };
  102.  
  103. const getTotalCharacters = () => {
  104. return document.querySelectorAll(".dash-letter").length - 1;
  105. };
  106.  
  107. function calculateRacesLastHour() {
  108. const oneHourAgo = Date.now() - 1000 * 60 * 60;
  109.  
  110. const recentRaces = raceTimes.filter(raceTime => raceTime.timestamp >= oneHourAgo);
  111.  
  112. return recentRaces.length;
  113. }
  114.  
  115. function calculateEstimatedRacesPerHour() {
  116. if (raceTimes.length === 0) return 0;
  117.  
  118. const avgRaceTimeInHours = (raceTimes.reduce((a, b) => a + b.duration, 0) / raceTimes.length) / (1000 * 60 * 60);
  119. const estimatedRacesPerHour = (1 / avgRaceTimeInHours).toFixed(1);
  120.  
  121. localStorage.setItem("estimatedRaces", estimatedRacesPerHour);
  122. return estimatedRacesPerHour;
  123. }
  124.  
  125. function resetSessionData() {
  126. sessionStartTime = Date.now();
  127. raceTimes = [];
  128.  
  129. document.getElementById("estimatedRaces").textContent = 0;
  130. document.getElementById("hourlyRaces").textContent = 0;
  131.  
  132. localStorage.setItem("sessionStartTime", sessionStartTime);
  133. localStorage.setItem("raceTimes", JSON.stringify(raceTimes));
  134. localStorage.setItem("estimatedRaces", 0);
  135.  
  136. console.log("Session data reset.");
  137. }
  138.  
  139. const updateSessionInfo = () => {
  140. const typedCharacters = getTypedCharacterCount();
  141. const totalCharactersInRace = getTotalCharacters();
  142.  
  143. if (typedCharacters === totalCharactersInRace && totalCharactersInRace > 0) {
  144. const currentTime = Date.now();
  145.  
  146. const persistData = localStorage.getItem("persist:nt");
  147. if (persistData) {
  148. const parsedPersistData = JSON.parse(persistData);
  149. const user = JSON.parse(parsedPersistData.user);
  150. sessionRaces = user.sessionRaces = (user.sessionRaces || 0) + 1;
  151. averageWPM = user.avgSpeed || 0;
  152. parsedPersistData.user = JSON.stringify(user);
  153. localStorage.setItem("persist:nt", JSON.stringify(parsedPersistData));
  154. }
  155.  
  156. const raceDuration = currentTime - (raceTimes.length > 0 ? raceTimes[raceTimes.length - 1].timestamp : sessionStartTime);
  157. raceTimes.push({ timestamp: currentTime, duration: raceDuration });
  158. localStorage.setItem("raceTimes", JSON.stringify(raceTimes));
  159.  
  160.  
  161. raceTimes = raceTimes.filter(race => race.timestamp >= (Date.now() - 1000 * 60 * 60));
  162.  
  163. document.getElementById("sessionValue").textContent = sessionRaces;
  164. document.getElementById("averageWPM").textContent = averageWPM;
  165.  
  166. document.getElementById("hourlyRaces").textContent = calculateRacesLastHour();
  167.  
  168. const estimatedRaces = calculateEstimatedRacesPerHour();
  169. document.getElementById("estimatedRaces").textContent = estimatedRaces;
  170.  
  171. clearInterval(updateUI);
  172. console.log("Race Complete. Session Races:", sessionRaces, "Average WPM:", averageWPM);
  173. }
  174. };
  175.  
  176. const interval = setInterval(() => {
  177. if (createPostRaceInfo()) {
  178. clearInterval(interval);
  179. }
  180. }, 500);
  181.  
  182. const updateUI = setInterval(() => {
  183. updateSessionInfo();
  184. }, 500);
  185. })();