presence.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. const presence = new Presence({
  2. clientId: "612416330003382314",
  3. }),
  4. browsingTimestamp = Math.floor(Date.now() / 1000);
  5. const enum Assets {
  6. Logo = "https://cdn.rcd.gg/PreMiD/websites/R/Roblox/assets/logo.png",
  7. DeveloperLogo = "https://cdn.rcd.gg/PreMiD/websites/R/Roblox/assets/0.png",
  8. CreateLogo = "https://cdn.rcd.gg/PreMiD/websites/R/Roblox/assets/1.png",
  9. }
  10. let devImage = false;
  11. presence.on("UpdateData", async () => {
  12. const [buttons, imagesEnabled, onlyDevForums] = await Promise.all([
  13. presence.getSetting<boolean>("buttons"),
  14. presence.getSetting<boolean>("images"),
  15. presence.getSetting<boolean>("only-devforum"),
  16. ]),
  17. presenceData: PresenceData = {
  18. details: "Unknown page",
  19. largeImageKey: Assets.Logo,
  20. startTimestamp: browsingTimestamp,
  21. },
  22. { pathname, hostname, href } = document.location,
  23. gameName = document.querySelector<HTMLHeadingElement>(
  24. "div.game-calls-to-action > div.game-title-container > h1"
  25. ),
  26. profileName = document.querySelector<HTMLHeadingElement>(".profile-name "),
  27. messageTab =
  28. document.querySelector("li.menu-option.ng-scope.active")?.textContent ??
  29. document.querySelector("li.rbx-tab.ng-scope.active")?.textContent,
  30. groupTab =
  31. document.querySelector<HTMLLIElement>(
  32. "#configure-group .tab-content-group ul .active"
  33. ) ??
  34. document.querySelector<HTMLLIElement>(
  35. "#horizontal-tabs li.rbx-tab.active"
  36. ),
  37. newUrl = new URL(href),
  38. searchResult =
  39. newUrl.searchParams?.get("Keyword") ?? newUrl.searchParams?.get("query"),
  40. item = document.querySelector<HTMLHeadingElement>(
  41. ".item-name-container h2"
  42. )?.textContent;
  43. switch (hostname) {
  44. case "web.roblox.com":
  45. case "www.roblox.com": {
  46. const pages: {
  47. [name: string]: PresenceData;
  48. } = {
  49. "/home": { state: "Home" },
  50. "/my/avatar": { state: "Avatar Editor" },
  51. "/feeds": { state: "Feed" },
  52. "/premium": { state: "Premium Membership" },
  53. "/promocodes": { state: "Promocodes" },
  54. "/redeem": { state: "Redeem" },
  55. "/giftcards": { state: "Gift Cards" },
  56. "/robux": { state: "Robux" },
  57. "/groups/join": { details: "Browsing groups..." },
  58. "/trades": { state: "Trades" },
  59. "/support": { state: "Support" },
  60. "/translator-portal": { state: "Translator Portal" },
  61. "/info/roblox-badges": { state: "Badges" },
  62. "/upgrades": { state: "Buying Product" },
  63. "/crossdevicelogin": { state: "Quick Log In" },
  64. "/abusereport/": { state: "Reporting Content Abuse" },
  65. "/user-ads/create": { state: "Creating Ad" },
  66. "/login": { state: "Log In" },
  67. };
  68. switch (true) {
  69. case !!document.querySelector(".notification-stream-container"): {
  70. presenceData.details = "Viewing Notifications";
  71. if (presenceData.state) delete presenceData.state;
  72. break;
  73. }
  74. case pathname.includes("/users") && pathname.includes("/profile"): {
  75. if (
  76. document
  77. .querySelector<HTMLAnchorElement>(
  78. "#horizontal-tabs li.rbx-tab.active a"
  79. )
  80. ?.textContent?.trim() === "Creations" // Profile tabs
  81. ) {
  82. presenceData.details = `Profile: ${profileName?.textContent}`;
  83. presenceData.state = "Browsing creations...";
  84. } else {
  85. presenceData.details = "Looking on a profile: ";
  86. presenceData.state = profileName?.textContent;
  87. }
  88. presenceData.largeImageKey =
  89. document
  90. .querySelector(".avatar-card-link.avatar-image-link")
  91. ?.querySelector("img")
  92. ?.getAttribute("src") ?? Assets.Logo;
  93. presenceData.buttons = [
  94. {
  95. label: "Visit Profile",
  96. url: href,
  97. },
  98. ];
  99. break;
  100. }
  101. case pathname.includes("/my/messages"):
  102. case pathname.includes("/My/Messages"): {
  103. presenceData.details = "Messages";
  104. presenceData.state = `Tab: ${messageTab}`;
  105. break;
  106. }
  107. case pathname.includes("/my/account"): {
  108. presenceData.details = "Settings";
  109. presenceData.state = `Tab: ${messageTab}`;
  110. break;
  111. }
  112. case pathname.includes("/users/friends"): {
  113. presenceData.details = "Friends";
  114. presenceData.state = `Tab: ${
  115. document.querySelector<HTMLAnchorElement>(".rbx-tab-heading.active")
  116. ?.textContent // Friends tab
  117. }`;
  118. break;
  119. }
  120. case pathname.includes("/users") && pathname.includes("/inventory"): {
  121. presenceData.details = "Inventory";
  122. presenceData.state = document.querySelector<HTMLLIElement>(
  123. "#vertical-menu > li.menu-option.ng-scope.active"
  124. )?.textContent; // Inventory tab
  125. break;
  126. }
  127. case pathname.includes("/groups") &&
  128. !pathname.includes("/search") &&
  129. !pathname.includes("/develop"): {
  130. presenceData.state = `Tab: ${groupTab.textContent}`;
  131. if (pathname.includes("/create"))
  132. presenceData.details = "Creating New Group";
  133. else if (pathname.includes("/configure"))
  134. presenceData.details = "Configuring Group";
  135. else {
  136. presenceData.details = document.querySelector<HTMLHeadingElement>(
  137. ".group-name.text-overflow"
  138. )?.textContent; // Groupname
  139. presenceData.largeImageKey =
  140. document.querySelector<HTMLImageElement>("div.group-image img")
  141. ?.src ?? Assets.Logo; // Groupimage
  142. presenceData.buttons = [
  143. {
  144. label: "Visit Group",
  145. url: href,
  146. },
  147. ];
  148. }
  149. break;
  150. }
  151. case pathname.includes("/search/groups"): {
  152. presenceData.details = "Searching for a group:";
  153. presenceData.state = new URL(href).searchParams.get("keyword");
  154. break;
  155. }
  156. case (pathname === "/discover/" || pathname === "/discover") &&
  157. gameName === null: {
  158. presenceData.details = "Browsing games...";
  159. if (presenceData.state) delete presenceData.state;
  160. if (searchResult) {
  161. presenceData.details = "Searching for a game: ";
  162. presenceData.smallImageKey = Assets.Search;
  163. presenceData.state = searchResult;
  164. }
  165. break;
  166. }
  167. case pathname.includes("/games/") &&
  168. !pathname.includes("/localization"): {
  169. presenceData.details = `Game: ${gameName?.textContent}`;
  170. presenceData.state = `Tab: ${
  171. document.querySelector<HTMLLIElement>(
  172. "#horizontal-tabs li.rbx-tab.active"
  173. )?.textContent // Gametab
  174. }`;
  175. presenceData.largeImageKey =
  176. document
  177. .querySelector("[class*='carousel-item'] > img")
  178. ?.getAttribute("src") ?? Assets.Logo;
  179. presenceData.buttons = [
  180. {
  181. label: "Visit Game",
  182. url: href,
  183. },
  184. ];
  185. break;
  186. }
  187. case pathname.includes("/catalog"): {
  188. const itemImage = document.querySelector<HTMLImageElement>(
  189. "span.thumbnail-span img"
  190. );
  191. if (searchResult) {
  192. presenceData.details = "Searching for an item: ";
  193. presenceData.smallImageKey = Assets.Search;
  194. presenceData.state = searchResult;
  195. } else if (itemImage) {
  196. presenceData.details = "Looking at Catalog Item:";
  197. presenceData.largeImageKey = itemImage?.src ?? Assets.Logo;
  198. presenceData.state = item;
  199. presenceData.buttons = [
  200. {
  201. label: "View Catalog Item",
  202. url: href,
  203. },
  204. ];
  205. } else {
  206. presenceData.details = "Current page:";
  207. presenceData.state = "Catalog";
  208. }
  209. break;
  210. }
  211. case pathname.includes("/places/"): {
  212. presenceData.details = "Configuring Place";
  213. presenceData.state = `Tab: ${
  214. document.querySelector<HTMLDivElement>(
  215. "#MasterContainer #navbar div.selected a"
  216. )?.textContent || "Unknown"
  217. }`;
  218. break;
  219. }
  220. case pathname.includes("/universes/configure"): {
  221. presenceData.details = "Configuring their experience";
  222. presenceData.state = `Tab: ${
  223. document.querySelector<HTMLDivElement>(
  224. "#MasterContainer #navbar div.selected a"
  225. )?.textContent || "Unknown"
  226. }`;
  227. break;
  228. }
  229. case pathname.includes("/bundles/"): {
  230. presenceData.details = "Looking at Bundle:";
  231. presenceData.largeImageKey =
  232. document.querySelector<HTMLImageElement>("span.thumbnail-span img")
  233. ?.src ?? Assets.Logo;
  234. presenceData.state = item;
  235. presenceData.buttons = [
  236. {
  237. label: "View Bundle",
  238. url: href,
  239. },
  240. ];
  241. break;
  242. }
  243. case pathname.includes("/search/users"): {
  244. presenceData.details = "Searching for an user:";
  245. presenceData.smallImageKey = Assets.Search;
  246. presenceData.state = new URL(href).searchParams.get("keyword");
  247. break;
  248. }
  249. case pathname.includes("/develop"): {
  250. presenceData.name = "Roblox - Developers";
  251. presenceData.details = "Viewing tab";
  252. const developTabs = document.querySelector<HTMLDivElement>(
  253. "#DevelopTabs .tab-active"
  254. )?.textContent;
  255. switch (developTabs) {
  256. case "My Creations": {
  257. presenceData.state = `${developTabs} > ${
  258. document.querySelector<HTMLAnchorElement>(".tab-item-selected")
  259. ?.textContent
  260. }`;
  261. break;
  262. }
  263. case "Group Creations": {
  264. presenceData.state = `${developTabs} > ${
  265. document.querySelector<HTMLAnchorElement>(
  266. '#SelectedGroupId option[selected="selected"]'
  267. )?.textContent
  268. } > ${
  269. document.querySelector<HTMLAnchorElement>(".tab-item-selected")
  270. ?.textContent
  271. }`;
  272. break;
  273. }
  274. case "Library": {
  275. if (searchResult) {
  276. presenceData.details = `Searching at ${developTabs} for: `;
  277. presenceData.state = searchResult;
  278. } else {
  279. presenceData.state = `${developTabs} > ${
  280. document.querySelector<HTMLAnchorElement>(
  281. ".selectedAssetTypeFilter"
  282. )?.textContent
  283. }`;
  284. }
  285. break;
  286. }
  287. default: {
  288. presenceData.state = `${developTabs}`;
  289. break;
  290. }
  291. }
  292. break;
  293. }
  294. case pathname.includes("/localization"): {
  295. const localizationTab =
  296. document.querySelector<HTMLSpanElement>(
  297. ".left-panel ul .active a"
  298. ) ??
  299. document.querySelector<HTMLSpanElement>(
  300. ".nav-tabs .active .text-lead"
  301. );
  302. if (pathname.includes("/configure"))
  303. presenceData.details = "Managing Localizations";
  304. else {
  305. const localizationGameName =
  306. document.querySelector<HTMLHeadingElement>(
  307. "#selenium-game-title-heading"
  308. ) ??
  309. document.querySelector<HTMLHeadingElement>(
  310. "div.component-container h4"
  311. );
  312. presenceData.details = `Localizing "${
  313. localizationGameName?.textContent ?? "Untilted Game"
  314. }"`;
  315. }
  316. presenceData.state = `Tab: ${localizationTab.textContent}`;
  317. break;
  318. }
  319. case pathname.includes("/transactions"): {
  320. presenceData.details = "Transactions Page";
  321. presenceData.state = `Tab: ${
  322. document.querySelector<HTMLSpanElement>(
  323. ".transaction-type-dropdown .rbx-selection-label"
  324. )?.textContent
  325. // Transaction tab
  326. }`;
  327. break;
  328. }
  329. case pathname.includes("/badges/"): {
  330. presenceData.details = "Looking at Badge:";
  331. presenceData.largeImageKey =
  332. document.querySelector<HTMLImageElement>("span.thumbnail-span img")
  333. ?.src ?? Assets.Logo;
  334. presenceData.state = item;
  335. presenceData.buttons = [
  336. {
  337. label: "View Badge",
  338. url: href,
  339. },
  340. ];
  341. break;
  342. }
  343. case pathname.includes("/library/"): {
  344. presenceData.details = "Looking at Asset:";
  345. presenceData.largeImageKey =
  346. document.querySelector<HTMLImageElement>("span.thumbnail-span img")
  347. ?.src ?? Assets.Logo;
  348. presenceData.state = item;
  349. presenceData.buttons = [
  350. {
  351. label: "View Asset",
  352. url: href,
  353. },
  354. ];
  355. break;
  356. }
  357. case pathname.includes("/game-pass/"): {
  358. presenceData.details = "Looking at Gamepass:";
  359. presenceData.largeImageKey =
  360. document.querySelector<HTMLImageElement>("span.thumbnail-span img")
  361. ?.src ?? Assets.Logo;
  362. presenceData.state = item;
  363. presenceData.buttons = [
  364. {
  365. label: "View Gamepass",
  366. url: href,
  367. },
  368. ];
  369. break;
  370. }
  371. default: {
  372. for (const [i, v] of Object.entries(pages)) {
  373. if (pathname.includes(i)) {
  374. presenceData.details = v.details ?? "Current Page: ";
  375. if (v.state) presenceData.state = v.state;
  376. else if (presenceData.buttons) delete presenceData.buttons;
  377. }
  378. }
  379. }
  380. }
  381. break;
  382. }
  383. case "devforum.roblox.com": {
  384. const pages: {
  385. [name: string]: PresenceData;
  386. } = {
  387. "/": { state: "Browsing Homepage" },
  388. "/following": { state: "Browsing Following Topics" },
  389. "/top": { state: "Browsing Top Topics" },
  390. "/unread": { state: "Browsing Unread Topics" },
  391. "/latest": { state: "Browsing Latest Topics" },
  392. "/new": { state: "Browsing New Topics" },
  393. "/about": { state: "Browsing About" },
  394. "/faq": { state: "Browsing FAQ" },
  395. "/categories": { state: "Browsing Categories" },
  396. };
  397. presenceData.name = "Roblox - DevForum";
  398. presenceData.details = "Browsing through the forum";
  399. presenceData.largeImageKey = Assets.DeveloperLogo;
  400. devImage = true;
  401. switch (true) {
  402. case pathname.includes("/t/"): {
  403. presenceData.state = `Reading ${
  404. document.querySelector(".fancy-title")?.textContent
  405. }`;
  406. presenceData.smallImageKey = Assets.Reading;
  407. presenceData.buttons = [
  408. {
  409. label: "View Topic",
  410. url: href,
  411. },
  412. ];
  413. break;
  414. }
  415. case pathname.includes("/tag/") ||
  416. (pathname.includes("/c/") && !pathname.includes("/categories/")): {
  417. presenceData.state = `Browsing ${
  418. document.title.split("- DevForum | Roblox")[0]
  419. }`;
  420. break;
  421. }
  422. case pathname.includes("/search"): {
  423. presenceData.state = `Searching "${new URL(href).searchParams.get(
  424. "q"
  425. )}"`;
  426. presenceData.smallImageKey = Assets.Search;
  427. break;
  428. }
  429. case pathname.includes("/badges") && !pathname.includes("/u"): {
  430. presenceData.state = "Browsing Badges";
  431. if (document.querySelector(".container.show-badge")) {
  432. presenceData.state = `Browsing ${
  433. document
  434. .querySelector(".container.show-badge h1")
  435. ?.textContent?.split("/")?.[1]
  436. } Badge`;
  437. }
  438. break;
  439. }
  440. case pathname.includes("/g/"): {
  441. presenceData.state = "Browsing Groups";
  442. if (document.querySelector(".group-info-name")) {
  443. presenceData.state = `Browsing ${
  444. document.querySelector(".group-info-name")?.textContent
  445. } Group`;
  446. }
  447. break;
  448. }
  449. case pathname.includes("/u/"): {
  450. const user = document.querySelector(".username")?.textContent;
  451. presenceData.state = `Browsing ${user}'s Profile`;
  452. presenceData.largeImageKey =
  453. document.querySelector<HTMLImageElement>(".user-profile-avatar img")
  454. ?.src ?? Assets.DeveloperLogo;
  455. devImage = true;
  456. presenceData.buttons = [
  457. {
  458. label: "View Profile",
  459. url: href,
  460. },
  461. ];
  462. const sections: {
  463. [name: string]: [string, boolean];
  464. } = {
  465. "/summary": [`Browsing ${user}'s Summary`, false],
  466. "/activity": [`Browsing ${user}'s Activity`, false],
  467. "/badges": [`Browsing ${user}'s Badges`, false],
  468. "/preferences": ["Editing Account Preferences", true],
  469. "/messages": ["Browsing Messages", true],
  470. "/notifications": ["Browsing Notifications", true],
  471. };
  472. if (pathname.includes("/follow")) {
  473. presenceData.state = "Browsing Network";
  474. if (presenceData.buttons) delete presenceData.buttons;
  475. if (pathname.includes("/followers"))
  476. presenceData.state = "Looking at Followers";
  477. else if (pathname.includes("/following"))
  478. presenceData.state = "Looking at Following";
  479. } else {
  480. for (const [i, v] of Object.entries(sections)) {
  481. if (pathname.includes(i)) {
  482. presenceData.state = v[0];
  483. if (v[1] === true && presenceData.buttons)
  484. delete presenceData.buttons;
  485. } else {
  486. for (const [i, v] of Object.entries(pages))
  487. if (pathname === i) presenceData.state = v.state;
  488. }
  489. }
  490. }
  491. break;
  492. }
  493. case !!document.querySelector(".composer-action-createTopic"): {
  494. presenceData.state = "Creating a New Topic";
  495. break;
  496. }
  497. case !!document.querySelector(".composer-action-privateMessage"): {
  498. presenceData.state = "Writing a Private Message";
  499. break;
  500. }
  501. case !!document.querySelector(".composer-action-reply"): {
  502. presenceData.state = `Replying To ${
  503. document
  504. .querySelector(".composer-action-reply")
  505. ?.querySelector(".user-link")?.textContent
  506. }`;
  507. break;
  508. }
  509. case !!document.querySelector(".keyboard-shortcuts-modal"): {
  510. presenceData.state = "Browsing Keyboard Shortcuts";
  511. break;
  512. }
  513. case !!document.querySelector(".flag-modal.in"): {
  514. presenceData.state = "Flagging a Post";
  515. if (presenceData.buttons) delete presenceData.buttons;
  516. break;
  517. }
  518. case !!document.querySelector(".composer-action-edit"): {
  519. presenceData.state = "Editing a Post";
  520. if (presenceData.buttons) delete presenceData.buttons;
  521. break;
  522. }
  523. }
  524. break;
  525. }
  526. case "create.roblox.com": {
  527. presenceData.name = "Roblox - Create";
  528. presenceData.largeImageKey = Assets.CreateLogo;
  529. const search = document.querySelector("#search-text-field");
  530. switch (true) {
  531. case pathname === "/landing": {
  532. presenceData.details = "Browsing on the landing page";
  533. break;
  534. }
  535. case pathname === "/": {
  536. presenceData.details = "Browsing on the homepage";
  537. break;
  538. }
  539. case pathname.includes("/dashboard/creations"): {
  540. presenceData.details = "Creation's dashboard";
  541. presenceData.state = `Tab: ${
  542. document.querySelector('button[aria-selected="true"]')?.textContent
  543. }`;
  544. break;
  545. }
  546. case pathname.includes("analytics"): {
  547. presenceData.details = "Viewing analytics";
  548. presenceData.state = `Tab: ${
  549. document.querySelector('button[aria-selected="true"]')?.textContent
  550. }`;
  551. break;
  552. }
  553. case pathname.includes("/translator-portal"): {
  554. presenceData.details = "Browsing trough the translator portal";
  555. break;
  556. }
  557. case pathname.includes("credentials"): {
  558. presenceData.details = "Viewing the credentails manager";
  559. break;
  560. }
  561. case pathname.includes("/docs"): {
  562. presenceData.name = "Roblox - Create - Docs";
  563. switch (true) {
  564. case !!search: {
  565. presenceData.details = search?.getAttribute("value")
  566. ? "Searching for:"
  567. : "Searching...";
  568. presenceData.state = document
  569. .querySelector("#search-text-field")
  570. ?.getAttribute("value");
  571. presenceData.smallImageKey = Assets.Search;
  572. break;
  573. }
  574. case !!document.querySelector('li[aria-selected="true"]'): {
  575. presenceData.details = "Reading docs about:";
  576. presenceData.state = document.querySelector(
  577. 'li[aria-selected="true"]'
  578. )?.textContent;
  579. presenceData.smallImageKey = Assets.Reading;
  580. presenceData.buttons = [
  581. {
  582. label: "Read Article",
  583. url: href,
  584. },
  585. ];
  586. break;
  587. }
  588. default: {
  589. presenceData.details = "Viewing the homepage";
  590. break;
  591. }
  592. }
  593. break;
  594. }
  595. case pathname.includes("/marketplace/asset/"): {
  596. presenceData.name = "Roblox - Create - Marketplace";
  597. presenceData.details = `Viewing ${document
  598. .querySelector('button[aria-selected="true"]')
  599. ?.textContent?.toLowerCase()}:`;
  600. presenceData.state = document.querySelector(
  601. '[data-testid="assetHeadingDetailsTestId"] > h1'
  602. )?.textContent;
  603. presenceData.buttons = [
  604. {
  605. label: "View Asset",
  606. url: href,
  607. },
  608. ];
  609. break;
  610. }
  611. case pathname.includes("/marketplace"): {
  612. presenceData.name = "Roblox - Create - Marketplace";
  613. presenceData.details = "Viewing tab";
  614. presenceData.state = document.querySelector(
  615. 'button[aria-selected="true"]'
  616. )?.textContent;
  617. presenceData.buttons = [
  618. {
  619. label: "View Marketplace",
  620. url: href,
  621. },
  622. ];
  623. break;
  624. }
  625. case pathname.includes("/talent/"): {
  626. presenceData.name = "Roblox - Create - Talent";
  627. if (document.querySelector("#text-input")?.getAttribute("value")) {
  628. presenceData.details = "Searching for:";
  629. presenceData.state = document
  630. .querySelector("#text-input")
  631. ?.getAttribute("value");
  632. presenceData.smallImageKey = Assets.Search;
  633. } else {
  634. presenceData.details = "Viewing tab";
  635. presenceData.state = document.querySelector(
  636. 'button[aria-selected="true"]'
  637. )?.textContent;
  638. }
  639. break;
  640. }
  641. case pathname.includes("/roadmap"): {
  642. presenceData.details = "Browsing through the roadmap";
  643. presenceData.buttons = [
  644. {
  645. label: "View Roadmap",
  646. url: href,
  647. },
  648. ];
  649. break;
  650. }
  651. }
  652. break;
  653. }
  654. }
  655. if (!buttons && presenceData.buttons) delete presenceData.buttons;
  656. if (
  657. !imagesEnabled &&
  658. presenceData.largeImageKey !== Assets.Logo &&
  659. !devImage &&
  660. hostname !== "create.roblox.com" // ImagesEnabled setting off & The largeimagekey isnt Assets.Logo & & Its NOT somewhere that uses the devimage
  661. )
  662. presenceData.largeImageKey = Assets.Logo;
  663. else if (
  664. !imagesEnabled &&
  665. presenceData.largeImageKey !== Assets.DeveloperLogo &&
  666. devImage // ImagesEnabled setting off & The largeimagekey isnt Assets.DeveloperLogo & Its somewhere that uses the devimage
  667. )
  668. presenceData.largeImageKey = Assets.DeveloperLogo;
  669. else if (
  670. !imagesEnabled &&
  671. presenceData.largeImageKey !== Assets.CreateLogo &&
  672. !devImage &&
  673. hostname === "create.roblox.com"
  674. )
  675. presenceData.largeImageKey = Assets.CreateLogo;
  676. if (onlyDevForums && !hostname.includes("devforum")) presence.clearActivity();
  677. else presence.setActivity(presenceData);
  678. });