presence.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. const presence = new Presence({
  2. clientId: "813781191308083239",
  3. }),
  4. time = Math.floor(Date.now() / 1000);
  5. presence.on("UpdateData", async () => {
  6. const title = document.title.replace(" - The New York Times", ""),
  7. setting = await getSettings(),
  8. { pathname, hostname, href, search } = window.location,
  9. path = pathname.split("/"),
  10. presenceData: PresenceData = {
  11. largeImageKey:
  12. "https://cdn.rcd.gg/PreMiD/websites/T/The%20New%20York%20Times/assets/logo.jpg",
  13. startTimestamp: time,
  14. };
  15. if (hostname === "www.nytimes.com") {
  16. if (setting.buttons && !setting.privacy) {
  17. presenceData.buttons = [
  18. {
  19. label: "View Page",
  20. url: href,
  21. },
  22. ];
  23. }
  24. if (
  25. ((path[1] === "international" || path[1] === "ca" || path[1] === "es") &&
  26. !path[2]) ||
  27. !path[1]
  28. )
  29. presenceData.details = "Viewing Home Page";
  30. else if (pathname.includes("/interactive/")) {
  31. presenceData.details = "Viewing an Interactive:";
  32. if (!setting.privacy) presenceData.state = title;
  33. } else if (
  34. pathname.includes("/section/") ||
  35. pathname.includes("/spotlight/podcasts")
  36. ) {
  37. presenceData.details = "Viewing a Section Page:";
  38. if (!setting.privacy) presenceData.state = title;
  39. } else if (pathname.includes("/destination/")) {
  40. presenceData.details = "Viewing a Destination Page:";
  41. if (!setting.privacy) presenceData.state = title;
  42. } else if (pathname.includes("/reviews/")) {
  43. presenceData.details = "Viewing a Review Page:";
  44. if (!setting.privacy) presenceData.state = title;
  45. } else if (pathname.includes("/column/")) {
  46. presenceData.details = "Viewing a Column Page:";
  47. if (!setting.privacy) presenceData.state = title;
  48. } else if (pathname.includes("/search")) {
  49. presenceData.details = setting.privacy ? "Searching" : "Searching for:";
  50. if (!setting.privacy)
  51. presenceData.state = new URLSearchParams(search).get("query");
  52. if (setting.buttons && !setting.privacy) {
  53. presenceData.buttons = [
  54. {
  55. label: "Show Search Results",
  56. url: href,
  57. },
  58. ];
  59. }
  60. } else if (pathname.includes("/video/")) {
  61. presenceData.details = "Viewing a Video Section:";
  62. presenceData.state = title;
  63. } else if (hasDatePath(pathname) && pathname.includes("/podcasts/")) {
  64. const audioPlayer = document.querySelector("audio"),
  65. podcast = document.querySelector("span.css-1f76qa2 span"),
  66. podcastLogo = document.querySelector<HTMLImageElement>(
  67. "span.css-1f76qa2 img"
  68. );
  69. presenceData.details = setting.privacy
  70. ? "Listening to a Podcast"
  71. : "Listening to a Podcast:";
  72. if (podcast && !setting.privacy)
  73. presenceData.state = `${podcast.textContent}: ${title}`;
  74. if (audioPlayer && !isNaN(audioPlayer.duration)) {
  75. [presenceData.startTimestamp, presenceData.endTimestamp] =
  76. presence.getTimestampsfromMedia(audioPlayer);
  77. if (audioPlayer.paused) {
  78. delete presenceData.endTimestamp;
  79. presenceData.startTimestamp = time;
  80. }
  81. }
  82. if (setting.buttons && !setting.privacy) {
  83. presenceData.buttons = [
  84. {
  85. label: "Listen to Podcast",
  86. url: href,
  87. },
  88. ];
  89. }
  90. if (setting.podcastLogo && !setting.privacy && podcastLogo)
  91. presenceData.largeImageKey = await getShortURL(podcastLogo.src);
  92. } else if (path[1] === "by" && path[2]) {
  93. const author =
  94. document.querySelector("h1.css-1uxfi68.e16wpn5v0")?.textContent ??
  95. "Unknown";
  96. presenceData.details = "Viewing an Author Page:";
  97. presenceData.state = author;
  98. if (document.querySelector("div.css-cnx41t img")) {
  99. presenceData.smallImageKey = await getShortURL(
  100. document.querySelector<HTMLImageElement>("div.css-cnx41t img").src
  101. );
  102. presenceData.smallImageText = author;
  103. }
  104. } else if (hasDatePath(pathname) && path[4]) {
  105. const author = document.querySelector<HTMLImageElement>(
  106. "img.css-1bfqq7u.ey68jwv2"
  107. ),
  108. authors = document.querySelector("p.css-aknsld.e1jsehar1"),
  109. headline =
  110. document.querySelector('h1[data-testid="headline"]')?.textContent ??
  111. title,
  112. isLive = document.querySelector(
  113. 'span span.css-bwjyn0.live-blog-header-live-label[data-active="true"]'
  114. ),
  115. wasLive = document.querySelector("span.css-233int.e16638kd4");
  116. presenceData.details = setting.privacy
  117. ? "Reading an Article"
  118. : setting.moreDetails && !isLive && !wasLive
  119. ? headline
  120. : "Reading an Article:";
  121. if (!setting.privacy) {
  122. presenceData.state =
  123. setting.moreDetails && !isLive && !wasLive
  124. ? `${authors?.textContent ?? `By ${author?.title ?? "Unknown"}`}, ${
  125. document.querySelector("time span")?.textContent
  126. }`
  127. : headline;
  128. }
  129. if (setting.buttons && !setting.privacy) {
  130. presenceData.buttons = [
  131. {
  132. label: "Read Article",
  133. url: href,
  134. },
  135. ];
  136. }
  137. if (isLive) {
  138. presenceData.smallImageKey = Assets.Live;
  139. presenceData.smallImageText = "Live";
  140. } else if (setting.articleAuthor && !setting.privacy && author) {
  141. presenceData.smallImageKey = await getShortURL(author.src);
  142. presenceData.smallImageText =
  143. authors?.textContent ?? `By ${author.title}`;
  144. }
  145. }
  146. } else if (hostname === "myaccount.nytimes.com") {
  147. presenceData.details = "Managing Account";
  148. if (!setting.privacy) {
  149. switch (path[2]) {
  150. case "subscription": {
  151. presenceData.state = "Subscription Overview";
  152. break;
  153. }
  154. case "billing": {
  155. presenceData.state = "Billing History";
  156. break;
  157. }
  158. case "settings": {
  159. presenceData.state = "Emails and Settings";
  160. break;
  161. }
  162. case "change-email": {
  163. presenceData.state = "Change Email";
  164. break;
  165. }
  166. case "forgot-password": {
  167. presenceData.state = "Reset Password";
  168. break;
  169. }
  170. default:
  171. if (path[1] === "get-started" && path[2] === "manage-billing")
  172. presenceData.state = "Payment details";
  173. }
  174. }
  175. }
  176. if (!presenceData.state) delete presenceData.state;
  177. if (!presenceData.details) presence.setActivity();
  178. else presence.setActivity(presenceData);
  179. });
  180. const shortenedURLs: Record<string, string> = {};
  181. async function getShortURL(url: string) {
  182. if (!url || url.length < 256) return url;
  183. if (shortenedURLs[url]) return shortenedURLs[url];
  184. try {
  185. const pdURL = await (
  186. await fetch(`https://pd.premid.app/create/${url}`)
  187. ).text();
  188. shortenedURLs[url] = pdURL;
  189. return pdURL;
  190. } catch (err) {
  191. presence.error(err);
  192. return url;
  193. }
  194. }
  195. function hasDatePath(pathname: string) {
  196. return /[0-9]{4}\/[0-9]{2}\/[0-9]{2}/g.test(pathname);
  197. }
  198. async function getSettings() {
  199. const settings = await Promise.all([
  200. presence.getSetting<boolean>("privacy"),
  201. presence.getSetting<boolean>("buttons"),
  202. presence.getSetting<boolean>("podcastLogo"),
  203. presence.getSetting<boolean>("articleAuthor"),
  204. presence.getSetting<boolean>("moreDetails"),
  205. ]),
  206. names = [
  207. "privacy",
  208. "buttons",
  209. "podcastLogo",
  210. "articleAuthor",
  211. "moreDetails",
  212. ],
  213. obj: {
  214. [key: string]: boolean;
  215. } = {};
  216. for (const [i, name] of names.entries()) obj[name] = settings[i];
  217. return obj;
  218. }