presence.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. const presence = new Presence({
  2. clientId: "721986767322087464",
  3. }),
  4. browsingTimestamp = Math.floor(Date.now() / 1000);
  5. const enum Assets {
  6. United = "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/0.png",
  7. Nforever = "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/1.png",
  8. Sunrise = "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/2.png",
  9. Original = "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/3.png",
  10. Nations = "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/4.png",
  11. Logo = "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/logo.png",
  12. }
  13. let currentURL = new URL(document.location.href),
  14. currentPath = currentURL.pathname.replace(/^\/|\/$/g, "").split("/"),
  15. presenceData: PresenceData = {
  16. details: "Viewing an unsupported page",
  17. largeImageKey:
  18. "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/logo.png",
  19. startTimestamp: browsingTimestamp,
  20. };
  21. const updateCallback = {
  22. _function: null as () => void,
  23. get function(): () => void {
  24. return this._function;
  25. },
  26. set function(parameter) {
  27. this._function = parameter;
  28. },
  29. get present(): boolean {
  30. return this._function !== null;
  31. },
  32. },
  33. /**
  34. * Initialize/reset presenceData.
  35. */
  36. resetData = (
  37. defaultData: PresenceData = {
  38. details: "Viewing an unsupported page",
  39. largeImageKey:
  40. "https://cdn.rcd.gg/PreMiD/websites/T/TrackMania%20Exchange/assets/logo.png",
  41. startTimestamp: browsingTimestamp,
  42. }
  43. ): void => {
  44. currentURL = new URL(document.location.href);
  45. currentPath = currentURL.pathname.replace(/^\/|\/$/g, "").split("/");
  46. presenceData = { ...defaultData };
  47. },
  48. /**
  49. * Search for URL parameters.
  50. * @param urlParam The parameter that you want to know about the value.
  51. */
  52. getURLParam = (urlParam: string): string => {
  53. return currentURL.searchParams.get(urlParam);
  54. };
  55. ((): void => {
  56. if (
  57. currentURL.hostname === "tm-exchange.com" ||
  58. currentURL.hostname === "www.tm-exchange.com"
  59. )
  60. presenceData.details = "On the home page";
  61. else if (currentURL.hostname === "blog.tm-exchange.com") {
  62. if (currentPath[0] === "post") {
  63. presenceData.details = "Reading a blog post";
  64. presenceData.state = document.querySelector(".WindowHeader1").textContent;
  65. } else if (currentPath[0] === "archive.aspx")
  66. presenceData.details = "Viewing the blog archive";
  67. else presenceData.details = "Viewing the blog";
  68. } else {
  69. let pageType: string,
  70. idPrefix = "ctl03";
  71. /*
  72. This part figures out the page type.
  73. There are three ways for getting it's type.
  74. The old structure are done as below.
  75. 1. From the "action" parameter on the current URL.
  76. 2. From the "action" parameter on the URL located on the "External Link" part on the top left corner.
  77. 3. From the "Location" part on the top left corner, specifically the bolded part.
  78. The new structure (only TMNF and TMUF) are done as below.
  79. 1. From the "action" path on the URL located on the "External Link" part on the top left corner.
  80. 2. From the "Location" part on the top left corner, specifically the bolded part.
  81. 3. From the "action" path on the current URL. (This is done last because the unrealibilty of the URL in some cases.)
  82. */
  83. const locationType: { [index: string]: string } = {
  84. Home: "home",
  85. Login: "login", // action guessed
  86. Registration: "register", // action guessed
  87. "Lost Login": "forget", // action guessed
  88. "Track Info": "trackshow",
  89. "Search Tracks": "tracksearch",
  90. "Nadeo Tracks": "tracksearch",
  91. "Your AOI": "tracksearch",
  92. "User's Tracks": "tracksearch",
  93. "Track Signs": "tracksigns",
  94. "Track Upload": "trackuploadtrack",
  95. "Submit Replays": "recordmassupload",
  96. Leaderboards: "userrecords",
  97. "Your Tracks": "tracksearch",
  98. "Your Replays": "tracksearch",
  99. "Your Downloads": "tracksearch",
  100. PlayPal: "playpal",
  101. "PlayPal On-Line": "playpalonline",
  102. TrackBeta: "trackbeta",
  103. "Find Users": "usersearch",
  104. "User Info": "usershow",
  105. "User Packs": "trackpacksearch",
  106. "Pack Info": "trackpackshow",
  107. "Your Account": "usershow",
  108. "Send Private Message": "postupdate",
  109. "Edit Post": "postedit",
  110. "Report Problem": "reportproblem",
  111. "News Archive": "newssearch",
  112. "Track Replay Info": "trackreplayshow",
  113. };
  114. if (
  115. currentURL.host === "united.tm-exchange.com" ||
  116. currentURL.host === "tmnforever.tm-exchange.com"
  117. ) {
  118. if (document.querySelector(".BookmarkCell a")) {
  119. currentURL = new URL(
  120. document.querySelector(".BookmarkCell a").textContent
  121. );
  122. currentPath = currentURL.pathname.replace(/^\/|\/$/g, "").split("/");
  123. [pageType] = currentPath;
  124. } else {
  125. try {
  126. pageType =
  127. locationType[
  128. document.querySelector(".NavigatorCell b").textContent
  129. ];
  130. } catch (e) {
  131. pageType = currentPath[0] || null;
  132. }
  133. }
  134. } else if (
  135. getURLParam("action") !== null &&
  136. getURLParam("action") !== "auto#auto"
  137. )
  138. pageType = getURLParam("action");
  139. else if (document.querySelector(".BookmarkCell a")) {
  140. currentURL = new URL(
  141. document.querySelector(".BookmarkCell a").textContent
  142. );
  143. pageType = getURLParam("action");
  144. } else {
  145. try {
  146. pageType =
  147. locationType[document.querySelector(".NavigatorCell b").textContent];
  148. } catch (e) {
  149. pageType = null;
  150. }
  151. }
  152. if (document.querySelector(".NavigatorCell b").textContent === "Login")
  153. pageType = "login";
  154. /* This parts gives suffix to the top text of the activity (aka details), to differentiate the different sites on the network. */
  155. switch (currentURL.host) {
  156. case "united.tm-exchange.com":
  157. presenceData.smallImageKey = Assets.United;
  158. presenceData.smallImageText = "United (TMUF-X)";
  159. idPrefix = "_ctl1";
  160. break;
  161. case "tmnforever.tm-exchange.com":
  162. presenceData.smallImageKey = Assets.Nforever;
  163. presenceData.smallImageText = "Nations Forever (TMNF-X)";
  164. idPrefix = "ctl01";
  165. break;
  166. case "nations.tm-exchange.com":
  167. presenceData.smallImageKey = Assets.Nations;
  168. presenceData.smallImageText = "Nations";
  169. break;
  170. case "sunrise.tm-exchange.com":
  171. presenceData.smallImageKey = Assets.Sunrise;
  172. presenceData.smallImageText = "Sunrise";
  173. break;
  174. case "original.tm-exchange.com":
  175. presenceData.smallImageKey = Assets.Original;
  176. presenceData.smallImageText = "Original";
  177. break;
  178. }
  179. /* This part sets the details to be given to PreMID. */
  180. if (
  181. currentPath[0] === "error" ||
  182. currentPath[0] === "errorhandler" ||
  183. (document.querySelector(".WindowTitle") &&
  184. document.querySelector(".WindowTitle").textContent === "Error") ||
  185. (document.querySelector("h1") &&
  186. document.querySelector("h1").textContent === "Server Error")
  187. )
  188. presenceData.details = "On a non-existent page";
  189. else {
  190. switch (pageType) {
  191. case "home": {
  192. presenceData.details = "On the home page";
  193. break;
  194. }
  195. case "login": {
  196. presenceData.details = "Logging in";
  197. break;
  198. }
  199. case "register": {
  200. presenceData.details = "Registering an account";
  201. break;
  202. }
  203. case "forget": {
  204. presenceData.details = "Figuring out the password";
  205. break;
  206. }
  207. case "trackshow": {
  208. presenceData.details = document.querySelector(
  209. `#${idPrefix}_ShowTrackName`
  210. ).textContent;
  211. presenceData.state = document.querySelector(
  212. "tr.WindowTableCell1:nth-child(3) > td:nth-child(2) > a:nth-child(3)"
  213. ).textContent;
  214. break;
  215. }
  216. case "tracksearch": {
  217. let searchSummary: string;
  218. if (
  219. document.querySelector(`#${idPrefix}_ShowSummary > b:nth-child(1)`)
  220. .textContent === "tracks"
  221. ) {
  222. searchSummary = document
  223. .querySelector(`#${idPrefix}_ShowSummary`)
  224. .textContent.slice(15, this.length - 4);
  225. } else {
  226. searchSummary = document
  227. .querySelector(`#${idPrefix}_ShowSummary`)
  228. .textContent.slice(8, this.length - 4);
  229. }
  230. presenceData.details = "Searching for a track";
  231. if (document.querySelector(".TextFilter")) {
  232. presenceData.state = `${document
  233. .querySelector(".TextFilter")
  234. .textContent.slice(9, this.length - 1)}, ${searchSummary}`;
  235. } else {
  236. presenceData.state =
  237. searchSummary[0].toUpperCase() + searchSummary.slice(1);
  238. }
  239. break;
  240. }
  241. case "tracksigns": {
  242. presenceData.details = "Viewing track signs";
  243. break;
  244. }
  245. case "trackuploadtrack": {
  246. presenceData.details = "Uploading a track";
  247. break;
  248. }
  249. case "recordmassupload": {
  250. presenceData.details = "Submitting replays";
  251. break;
  252. }
  253. case "userrecords": {
  254. const searchSummary = document
  255. .querySelector(`#${idPrefix}_ShowSummary`)
  256. .textContent.slice(16, this.length - 4);
  257. presenceData.details = "Viewing the leaderboards";
  258. if (
  259. (document.querySelector(`#${idPrefix}_GetUser`) as HTMLInputElement)
  260. .value
  261. ) {
  262. presenceData.state = `${
  263. (
  264. document.querySelector(
  265. `#${idPrefix}_GetUser`
  266. ) as HTMLInputElement
  267. ).value
  268. }, ${searchSummary}`;
  269. } else {
  270. presenceData.state =
  271. searchSummary[0].toUpperCase() + searchSummary.slice(1);
  272. }
  273. break;
  274. }
  275. case "forumshow":
  276. case "forumsshow": {
  277. presenceData.details = "Viewing the forums";
  278. if (pageType === "forumshow") {
  279. presenceData.state = document
  280. .querySelector(".WindowTitle")
  281. .textContent.trim();
  282. }
  283. break;
  284. }
  285. case "threadshow": {
  286. presenceData.details = "Viewing a thread";
  287. presenceData.state = document.querySelector(
  288. `#${idPrefix}_ShowSubject`
  289. ).textContent;
  290. break;
  291. }
  292. case "playpal": {
  293. presenceData.details = "Viewing PlayPal";
  294. break;
  295. }
  296. case "playpalonline": {
  297. presenceData.details = "Viewing PlayPal Online";
  298. break;
  299. }
  300. case "trackbeta": {
  301. presenceData.details = "Viewing TrackBeta";
  302. break;
  303. }
  304. case "usersearch": {
  305. const searchSummary = document
  306. .querySelector(`#${idPrefix}_ShowSummary`)
  307. .textContent.slice(15, this.length - 4);
  308. presenceData.details = "Searching for a user";
  309. if (document.querySelector(`#${idPrefix}_ShowName`)) {
  310. presenceData.state = `${
  311. document.querySelector(`#${idPrefix}_ShowName`).textContent
  312. }, ${searchSummary}`;
  313. } else {
  314. presenceData.state =
  315. searchSummary[0].toUpperCase() + searchSummary.slice(1);
  316. }
  317. break;
  318. }
  319. case "usershow": {
  320. presenceData.details = "Viewing a user's info";
  321. presenceData.state = document.querySelector(
  322. `#${idPrefix}_ShowLoginId`
  323. ).textContent;
  324. break;
  325. }
  326. case "trackpacksearch": {
  327. const searchSummary = document
  328. .querySelector(`#${idPrefix}_ShowSummary`)
  329. .textContent.slice(20, this.length - 4);
  330. presenceData.details = "Searching for a user pack";
  331. if (document.querySelector(`#${idPrefix}_ShowName`)) {
  332. presenceData.state = `${
  333. document.querySelector(`#${idPrefix}_ShowName`).textContent
  334. }, ${searchSummary}`;
  335. } else {
  336. presenceData.state =
  337. searchSummary[0].toUpperCase() + searchSummary.slice(1);
  338. }
  339. break;
  340. }
  341. case "postupdate": {
  342. presenceData.details = "Writing a private message";
  343. break;
  344. }
  345. case "trackpackshow": {
  346. presenceData.details = "Viewing a track pack";
  347. presenceData.state = `${
  348. document.querySelector(`#${idPrefix}_ShowPackName`).textContent
  349. } by ${
  350. document.querySelector(
  351. "#Table7 > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2) > a:nth-child(3)"
  352. ).textContent
  353. }`;
  354. break;
  355. }
  356. case "postedit": {
  357. presenceData.details = "Editing a post";
  358. break;
  359. }
  360. case "reportproblem": {
  361. presenceData.details = "Reporting something";
  362. break;
  363. }
  364. case "newssearch": {
  365. presenceData.details = "Viewing the news archive";
  366. break;
  367. }
  368. case "trackreplayshow": {
  369. presenceData.details = "Viewing the replay history";
  370. presenceData.state = document.querySelector(
  371. `#${idPrefix}_Windowrow10 a`
  372. ).textContent;
  373. break;
  374. }
  375. // No default
  376. }
  377. }
  378. }
  379. })();
  380. if (updateCallback.present) {
  381. const defaultData = { ...presenceData };
  382. presence.on("UpdateData", async () => {
  383. resetData(defaultData);
  384. updateCallback.function();
  385. presence.setActivity(presenceData);
  386. });
  387. } else {
  388. presence.on("UpdateData", async () => {
  389. presence.setActivity(presenceData);
  390. });
  391. }