presence.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. const presence = new Presence({
  2. clientId: "676560908578717702",
  3. }),
  4. userInfo = JSON.parse(JSON.parse(localStorage["persist:nt"]).user);
  5. let pageVariables: PageVarsType, carImage: string;
  6. function getNumberWithOrdinal(n: number): string {
  7. const s = ["th", "st", "nd", "rd"],
  8. v = n % 100;
  9. return n + (s[(v - 20) % 10] || s[v] || s[0]);
  10. }
  11. async function resizeImage(image: string): Promise<string> {
  12. return new Promise(resolve => {
  13. const img = new Image(),
  14. wh = 320;
  15. img.crossOrigin = "anonymous";
  16. img.src = image;
  17. img.onload = function () {
  18. let newWidth, newHeight, offsetX, offsetY;
  19. if (img.width > img.height) {
  20. newWidth = wh;
  21. newHeight = (wh / img.width) * img.height;
  22. offsetX = 0;
  23. offsetY = (wh - newHeight) / 2;
  24. } else {
  25. newHeight = wh;
  26. newWidth = (wh / img.height) * img.width;
  27. offsetX = (wh - newWidth) / 2;
  28. offsetY = 0;
  29. }
  30. const tempCanvas = document.createElement("canvas");
  31. tempCanvas.width = wh;
  32. tempCanvas.height = wh;
  33. tempCanvas
  34. .getContext("2d")
  35. .drawImage(img, offsetX, offsetY, newWidth, newHeight);
  36. resolve(tempCanvas.toDataURL("image/png"));
  37. };
  38. });
  39. }
  40. const enum Assets {
  41. Logo = "https://cdn.rcd.gg/PreMiD/websites/N/Nitro%20Type/assets/logo.png",
  42. User = "https://cdn.rcd.gg/PreMiD/websites/N/Nitro%20Type/assets/0.png",
  43. Guest = "https://cdn.rcd.gg/PreMiD/websites/N/Nitro%20Type/assets/1.png",
  44. }
  45. type PageVarsType = {
  46. "NTGLOBALS.CAR_PAINTED_URL": string;
  47. "NTGLOBALS.CAR_URL": string;
  48. "NTGLOBALS.CARS": [
  49. {
  50. carID: number;
  51. options: {
  52. largeSrc: string;
  53. smallSrc: string;
  54. };
  55. }
  56. ];
  57. };
  58. presence.on("UpdateData", async () => {
  59. if (!pageVariables) {
  60. pageVariables = await presence.getPageVariable(
  61. "NTGLOBALS.CAR_PAINTED_URL",
  62. "NTGLOBALS.CAR_URL",
  63. "NTGLOBALS.CARS"
  64. );
  65. }
  66. const largeSrc = pageVariables["NTGLOBALS.CARS"]
  67. .find(car => car.carID === userInfo.carID)
  68. .options.largeSrc.split("."),
  69. loggedIn = !!document.querySelector(".dropdown--account span"),
  70. { pathname, href, origin } = document.location,
  71. pathArr = pathname.split("/");
  72. if (!carImage) {
  73. carImage = await resizeImage(
  74. `${origin}${
  75. userInfo.carHueAngle
  76. ? pageVariables["NTGLOBALS.CAR_PAINTED_URL"]
  77. : pageVariables["NTGLOBALS.CAR_URL"]
  78. }${largeSrc[0]}${
  79. userInfo.carHueAngle ? `_${userInfo.carHueAngle}` : ""
  80. }.${largeSrc[1]}`
  81. );
  82. }
  83. const showCar = await presence.getSetting<boolean>("showCar"),
  84. presenceData: PresenceData = {
  85. largeImageKey:
  86. (loggedIn && showCar ? carImage : Assets.Logo) ?? Assets.Logo,
  87. smallImageKey: loggedIn ? Assets.User : Assets.Guest,
  88. smallImageText: loggedIn
  89. ? document.querySelector(".dropdown--account span").textContent
  90. : "Racing as a guest",
  91. };
  92. switch (pathArr[1]) {
  93. case "": {
  94. presenceData.details = "On the Homepage";
  95. break;
  96. }
  97. case "login": {
  98. presenceData.details = "Logging in";
  99. break;
  100. }
  101. case "signup": {
  102. presenceData.details = "Logging in";
  103. break;
  104. }
  105. case "garage": {
  106. presenceData.details = "Hanging in the Garage";
  107. if (pathArr[2] === "customizer") {
  108. presenceData.state = `Customising ${document
  109. .querySelector(".customizer--tab.is-current .customizer--tab--label")
  110. .textContent.replace(/s$/, "")
  111. .toLowerCase()}`;
  112. }
  113. break;
  114. }
  115. case "leagues": {
  116. const leagueName =
  117. document.querySelector(
  118. ".league-ranking--icon:not(.is-small) .league-ranking--icon--name"
  119. )?.textContent ?? "Legend",
  120. leaguePlacement = getNumberWithOrdinal(
  121. parseInt(
  122. document.querySelector(
  123. ".is-self .leagues--standings--place-indicator"
  124. ).textContent ||
  125. document.querySelector<HTMLImageElement>(
  126. ".is-self .leagues--standings--place-indicator img"
  127. ).alt
  128. )
  129. );
  130. presenceData.details = `Viewing ${
  131. document.querySelector(".card-cap > h1").childNodes[1].textContent
  132. } League standings`;
  133. presenceData.state = `League: ${leagueName} | Placement: ${leaguePlacement}`;
  134. break;
  135. }
  136. case "racer": {
  137. presenceData.details = "Viewing Racer Profiles";
  138. presenceData.state = document.querySelector(
  139. ".player-name--container"
  140. ).textContent;
  141. presenceData.buttons = [
  142. {
  143. url: href,
  144. label: "View Profile",
  145. },
  146. ];
  147. if (
  148. document.querySelector<HTMLAnchorElement>(".player-name--tag")?.href
  149. ) {
  150. presenceData.buttons.push({
  151. url: document.querySelector<HTMLAnchorElement>(".player-name--tag")
  152. ?.href,
  153. label: "View Team",
  154. });
  155. }
  156. break;
  157. }
  158. case "team": {
  159. switch (pathArr[2]) {
  160. case "create": {
  161. presenceData.details = "Creating a team";
  162. break;
  163. }
  164. default: {
  165. if (typeof pathArr[2] === "undefined")
  166. presenceData.details = "Browsing teams";
  167. else {
  168. presenceData.details = "Viewing team Info";
  169. presenceData.state = document.querySelector("h1.h2");
  170. presenceData.buttons = [
  171. {
  172. label: "View Page",
  173. url: href,
  174. },
  175. ];
  176. }
  177. }
  178. }
  179. break;
  180. }
  181. case "achievements": {
  182. presenceData.details = "Browsing achievements";
  183. const pName = document.querySelector(
  184. ".has-btn--vertical .btn.is-active"
  185. ).textContent;
  186. presenceData.state = `${pName} (${(pName === "Summary"
  187. ? document.querySelector(".prog-points").textContent
  188. : document.querySelector(".twb").textContent
  189. ).replaceAll(" ", "")})`;
  190. break;
  191. }
  192. case "race": {
  193. presenceData.details = "Racing";
  194. presenceData.state = `${getNumberWithOrdinal(
  195. parseInt(document.querySelector(".dash-pos .tsxxl").textContent)
  196. )} ${document
  197. .querySelector(".list--xs > li:nth-child(1) > div:nth-child(1) ")
  198. .textContent.split("\n")
  199. .reverse()
  200. .join("")
  201. .toLowerCase()} ${`${
  202. document.querySelector(
  203. ".list--xs > li:nth-child(2) > div:nth-child(1) > div:nth-child(2)"
  204. ).textContent
  205. }acc`}`;
  206. if (
  207. document.querySelector(".raceLight-status") ||
  208. document.querySelector(".waiting-for-leader--status--heading")
  209. )
  210. presenceData.state = "Waiting for the race to start.";
  211. if (document.querySelector(".race-results")) {
  212. let stats = "";
  213. for (const item of document.querySelectorAll(
  214. ".gridTable-row.is-self .list-item"
  215. ))
  216. stats += `${item.textContent} `;
  217. stats = stats.slice(0, -1).replace(/\(\+[0-9.]+\)/, "");
  218. presenceData.details = `Finished race ${
  219. document.querySelector("div.raceResults-title").textContent
  220. }`;
  221. presenceData.state = stats;
  222. }
  223. break;
  224. }
  225. case "season": {
  226. let tier: string;
  227. for (const reward of document.querySelectorAll(".is-complete"))
  228. tier = reward?.querySelector(".seasonReward-tier")?.textContent ?? "0";
  229. presenceData.details = "Viewing current season";
  230. presenceData.state = `${
  231. document.querySelector(".season-header h1").textContent
  232. } (Tier ${tier} | ${
  233. document.querySelector<HTMLDivElement>(".prog-barFill").style.width
  234. })`;
  235. break;
  236. }
  237. case "friends": {
  238. presenceData.details = "Browsing friends";
  239. break;
  240. }
  241. case "shop": {
  242. presenceData.details = "Browsing shop";
  243. const selectedShop = document.querySelector<HTMLImageElement>(
  244. ".page-shop--dealership-option.is-selected img"
  245. ).alt;
  246. switch (selectedShop) {
  247. case "Level 1": {
  248. presenceData.state = "Loh's Cars";
  249. break;
  250. }
  251. case "Level 2": {
  252. presenceData.state = "Midtown Motors";
  253. break;
  254. }
  255. case "Level 3": {
  256. presenceData.state = "Hai Auto";
  257. break;
  258. }
  259. default: {
  260. presenceData.state = selectedShop;
  261. break;
  262. }
  263. }
  264. break;
  265. }
  266. case "profile": {
  267. presenceData.details = "Updating Racer Profile";
  268. break;
  269. }
  270. case "news": {
  271. presenceData.details = "Browsing the News";
  272. break;
  273. }
  274. case "stats": {
  275. presenceData.details = "Viewing Stats";
  276. break;
  277. }
  278. case "racelog": {
  279. presenceData.details = "Browsing race logs";
  280. presenceData.state = document.querySelector(".tab.is-active");
  281. break;
  282. }
  283. case "support": {
  284. presenceData.details = "Checking the Support Page";
  285. break;
  286. }
  287. default: {
  288. presenceData.details = pathname;
  289. }
  290. }
  291. if (!presenceData.details) {
  292. presence.error("no presence!");
  293. presence.setActivity();
  294. } else presence.setActivity(presenceData);
  295. });