presence.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. const presence = new Presence({
  2. clientId: "655247212728811530",
  3. });
  4. let currentURL = new URL(document.location.href),
  5. currentPath = currentURL.pathname.replace(/^\/|\/$/g, "").split("/");
  6. const browsingTimestamp = Math.floor(Date.now() / 1000);
  7. let presenceData: PresenceData = {
  8. details: "Viewing an unsupported page",
  9. largeImageKey:
  10. "https://cdn.rcd.gg/PreMiD/websites/T/Typeracer/assets/logo.png",
  11. startTimestamp: browsingTimestamp,
  12. };
  13. const updateCallback = {
  14. _function: null as () => void,
  15. get function(): () => void {
  16. return this._function;
  17. },
  18. set function(parameter) {
  19. this._function = parameter;
  20. },
  21. get present(): boolean {
  22. return this._function !== null;
  23. },
  24. },
  25. resetData = (
  26. defaultData: PresenceData = {
  27. details: "Viewing an unsupported page",
  28. largeImageKey:
  29. "https://cdn.rcd.gg/PreMiD/websites/T/Typeracer/assets/logo.png",
  30. startTimestamp: browsingTimestamp,
  31. }
  32. ): void => {
  33. currentURL = new URL(document.location.href);
  34. currentPath = currentURL.pathname.replace(/^\/|\/$/g, "").split("/");
  35. presenceData = { ...defaultData };
  36. };
  37. ((): void => {
  38. let raceStamp: number = null;
  39. if (currentURL.hostname === "play.typeracer.com") {
  40. updateCallback.function = (): void => {
  41. if (document.querySelector(".gameView")) {
  42. presenceData.details = "Playing a race";
  43. const gameStatusLabel =
  44. document.querySelector(".gameStatusLabel").textContent;
  45. switch (gameStatusLabel) {
  46. case "Waiting for more people...": {
  47. presenceData.state = "Waiting for more people...";
  48. if (raceStamp === null) raceStamp = Math.floor(Date.now() / 1000);
  49. presenceData.startTimestamp = raceStamp;
  50. break;
  51. }
  52. case "The race is about to start!": {
  53. presenceData.state = "Counting down...";
  54. presenceData.endTimestamp =
  55. Math.floor(Date.now() / 1000) +
  56. Number(
  57. document
  58. .querySelector(".countdownPopup .time")
  59. .textContent.slice(1)
  60. );
  61. raceStamp = null;
  62. break;
  63. }
  64. case "The race is on! Type the text below:":
  65. case "Go!": {
  66. const textBox = document.querySelector(
  67. "table.gameView > tbody > tr:nth-child(2) > td > table > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td > div > div"
  68. );
  69. let lettersTyped = 0;
  70. for (const i in textBox.children) {
  71. if (
  72. typeof textBox.children[i] !== "number" &&
  73. typeof textBox.children[i] !== "function" &&
  74. getComputedStyle(textBox.children[i]).color ===
  75. "rgb(153, 204, 0)"
  76. )
  77. lettersTyped += textBox.children[i].textContent.length;
  78. }
  79. presenceData.state = `${
  80. Math.round((lettersTyped / textBox.textContent.length) * 10000) /
  81. 100
  82. }%, ${document
  83. .querySelector(".rankPanelWpm-self")
  84. .textContent.toUpperCase()}`;
  85. if (raceStamp === null) raceStamp = Math.floor(Date.now() / 1000);
  86. presenceData.startTimestamp = raceStamp;
  87. break;
  88. }
  89. default:
  90. if (
  91. gameStatusLabel === "The race has ended." ||
  92. gameStatusLabel.startsWith("You finished")
  93. ) {
  94. presenceData.details = "Just finished with a race";
  95. presenceData.state = `${document
  96. .querySelector(".rankPanelWpm-self")
  97. .textContent.toUpperCase()}, ${
  98. document.querySelector(
  99. ".tblOwnStats > tbody:nth-child(2) > tr:nth-child(3) > td:nth-child(2)"
  100. ).textContent
  101. } acc., ${
  102. document.querySelector(
  103. ".tblOwnStats > tbody:nth-child(2) > tr:nth-child(2) > td:nth-child(2)"
  104. ).textContent
  105. }`;
  106. presenceData.startTimestamp = browsingTimestamp;
  107. }
  108. }
  109. } else presenceData.details = "Viewing the home page";
  110. };
  111. } else if (currentURL.hostname === "data.typeracer.com") {
  112. /*
  113. Part 2
  114. data.typeracer.com (pit stop and misc. pages)
  115. */
  116. switch (currentPath[0]) {
  117. case "pit": {
  118. switch (currentPath[1]) {
  119. case "profile": {
  120. presenceData.details = "Viewing a racer profile";
  121. presenceData.state =
  122. document.querySelector("#profileUsername").textContent || null;
  123. break;
  124. }
  125. case "text_info": {
  126. presenceData.details = "Viewing a text";
  127. presenceData.state = currentURL.searchParams.get("id");
  128. break;
  129. }
  130. case "result": {
  131. presenceData.details = "Viewing a race result";
  132. presenceData.state = `Race ${
  133. currentURL.searchParams.get("id").split("|")[2]
  134. } of ${currentURL.searchParams.get("id").split("|")[1].slice(3)}`;
  135. break;
  136. }
  137. case "race_history": {
  138. presenceData.details = "Viewing someone's race history";
  139. presenceData.state = currentURL.searchParams.get("user") || null;
  140. break;
  141. }
  142. case "home": {
  143. presenceData.details = "Viewing the pit stop";
  144. break;
  145. }
  146. case "competitions": {
  147. presenceData.details = "Viewing the competition result";
  148. const strong = document
  149. .querySelector("div.themeContent > div:nth-child(5) > strong")
  150. .textContent.trim()
  151. .slice(0, -1)
  152. .split(" ");
  153. switch (
  154. document.querySelector("option[selected]").textContent.trim()
  155. ) {
  156. case "day": {
  157. presenceData.state = strong.join(" ");
  158. break;
  159. }
  160. case "week": {
  161. presenceData.state = `${strong[1]} ${strong[2]}, ${strong[4]}`;
  162. break;
  163. }
  164. case "month": {
  165. presenceData.state = `${strong[3]} ${strong[4]}`;
  166. break;
  167. }
  168. case "year":
  169. {
  170. [, presenceData.state] = strong;
  171. // No default
  172. }
  173. break;
  174. }
  175. break;
  176. }
  177. case "login": {
  178. presenceData.details = "Logging in";
  179. break;
  180. }
  181. default: {
  182. // eslint-disable-next-line no-one-time-vars/no-one-time-vars
  183. const pageNames: { [index: string]: string } = {
  184. // eslint-disable-next-line camelcase
  185. upgrade_account: "Upgrade your account",
  186. tos: "Terms of Service",
  187. // eslint-disable-next-line camelcase
  188. privacy_poicy: "Privacy Policy",
  189. };
  190. presenceData.details = "Viewing a page";
  191. presenceData.state = pageNames[currentPath[1]];
  192. }
  193. }
  194. break;
  195. }
  196. case "misc": {
  197. if (currentPath[1] === "about") {
  198. presenceData.details = "Viewing a page";
  199. presenceData.state = "About";
  200. }
  201. break;
  202. }
  203. case "admin":
  204. {
  205. presenceData.details = "Viewing school admin pages";
  206. // No default
  207. }
  208. break;
  209. }
  210. }
  211. })();
  212. if (updateCallback.present) {
  213. const defaultData = { ...presenceData };
  214. presence.on("UpdateData", async () => {
  215. resetData(defaultData);
  216. updateCallback.function();
  217. presence.setActivity(presenceData);
  218. });
  219. } else {
  220. presence.on("UpdateData", async () => {
  221. presence.setActivity(presenceData);
  222. });
  223. }