presence.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. const presence = new Presence({
  2. clientId: "839409255979155516",
  3. }),
  4. getStrings = async () => {
  5. return presence.getStrings(
  6. {
  7. play: "general.playing",
  8. pause: "general.paused",
  9. browse: "general.browsing",
  10. searchFor: "general.searchFor",
  11. searchSomething: "general.searchSomething",
  12. viewEpisode: "general.buttonViewEpisode",
  13. viewAnime: "general.viewAnime",
  14. viewSeries: "general.buttonViewSeries",
  15. viewAccount: "general.viewAccount",
  16. viewMovie: "general.viewMovie",
  17. buttonViewMovie: "general.buttonViewMovie",
  18. watchMovie: "general.watchingMovie",
  19. watchSeries: "general.watchingSeries",
  20. },
  21. await presence.getSetting<string>("lang").catch(() => "en")
  22. );
  23. },
  24. data: {
  25. oldLang?: string;
  26. startedSince?: number;
  27. meta?: {
  28. [key: string]: string;
  29. };
  30. settings?: {
  31. id?: string;
  32. delete?: boolean;
  33. value?: boolean;
  34. uses?: (keyof PresenceData)[];
  35. presence?: {
  36. page: string;
  37. uses?: keyof PresenceData;
  38. setTo?: string;
  39. if?:
  40. | {
  41. k: boolean;
  42. v?: string;
  43. delete?: boolean;
  44. }
  45. | {
  46. k: boolean;
  47. v?: string;
  48. delete?: boolean;
  49. }[];
  50. replace?: {
  51. input: string;
  52. output: string;
  53. }[];
  54. }[];
  55. }[];
  56. presence: {
  57. [key: string]: {
  58. disabled?: boolean;
  59. setPresenceData?: () => void;
  60. };
  61. };
  62. presenceData: PresenceData;
  63. } = {
  64. presence: {},
  65. meta: {},
  66. oldLang: "",
  67. startedSince: ~~(Date.now() / 1000),
  68. presenceData: {
  69. type: ActivityType.Watching,
  70. largeImageKey:
  71. "https://cdn.rcd.gg/PreMiD/websites/B/BetterAnime/assets/logo.png",
  72. smallImageKey: Assets.Search,
  73. },
  74. };
  75. let strings: Awaited<ReturnType<typeof getStrings>>,
  76. video: {
  77. duration: number;
  78. currentTime: number;
  79. paused: boolean;
  80. };
  81. presence.on("iFrameData", (data: typeof video) => {
  82. if (data) video = data;
  83. });
  84. presence.on("UpdateData", async () => {
  85. const [
  86. newLang,
  87. privacy,
  88. anime,
  89. movie,
  90. browse,
  91. timestamp,
  92. AnimeState,
  93. MovieState,
  94. ] = await Promise.all([
  95. presence.getSetting<string>("lang").catch(() => "en"),
  96. presence.getSetting<boolean>("privacy"),
  97. presence.getSetting<boolean>("anime"),
  98. presence.getSetting<boolean>("movie"),
  99. presence.getSetting<boolean>("browse"),
  100. presence.getSetting<boolean>("timestamp"),
  101. presence.getSetting<string>("AnimeState"),
  102. presence.getSetting<string>("MovieState"),
  103. ]),
  104. { pathname, search, href } = document.location;
  105. if (data.oldLang !== newLang || !strings) {
  106. data.oldLang = newLang;
  107. strings = await getStrings();
  108. }
  109. if (browse) data.presenceData.details = strings.browse;
  110. if (
  111. timestamp &&
  112. !data.presenceData.startTimestamp &&
  113. !data.presenceData.endTimestamp
  114. )
  115. data.presenceData.startTimestamp = data.startedSince;
  116. data.presence = {
  117. "/anime/(dublado|legendado)/([a-zA-Z0-9-]+)/([a-z-0-9]+)": {
  118. disabled: !anime,
  119. async setPresenceData() {
  120. data.meta.episode = document.querySelector(
  121. "div.anime-title > h3"
  122. )?.textContent;
  123. data.meta.title = document
  124. .querySelector("div.anime-title")
  125. ?.textContent?.replace(data.meta.episode, "");
  126. data.presenceData.smallImageKey = video.paused
  127. ? Assets.Pause
  128. : Assets.Play;
  129. data.presenceData.smallImageText = video.paused
  130. ? (await strings).pause
  131. : (await strings).play;
  132. [data.presenceData.startTimestamp, data.presenceData.endTimestamp] =
  133. presence.getTimestamps(video.currentTime, video.duration);
  134. const seriesURL = document.querySelector<HTMLAnchorElement>(
  135. "div.anime-title > h2 > a"
  136. )?.href;
  137. if (!seriesURL) {
  138. data.presenceData.buttons = [
  139. {
  140. label: (await strings).viewEpisode,
  141. url: href,
  142. },
  143. ];
  144. } else {
  145. data.presenceData.buttons = [
  146. {
  147. label: (await strings).viewEpisode,
  148. url: href,
  149. },
  150. {
  151. label: (await strings).viewSeries,
  152. url: seriesURL,
  153. },
  154. ];
  155. }
  156. if (video.paused) {
  157. delete data.presenceData.endTimestamp;
  158. delete data.presenceData.startTimestamp;
  159. }
  160. },
  161. },
  162. "/anime/(dublado|legendado)/([a-zA-Z0-9-]+)": {
  163. disabled: privacy || !anime,
  164. async setPresenceData() {
  165. data.presenceData.details = (await strings).viewAnime;
  166. data.presenceData.state = document.querySelector(
  167. "div.infos_left > div > h2"
  168. )?.textContent;
  169. data.presenceData.buttons = [
  170. {
  171. label: (await strings).viewSeries,
  172. url: href,
  173. },
  174. ];
  175. },
  176. },
  177. "/filme/(dublado|legendado)/([a-zA-Z0-9-]+)/([a-z-]+)": {
  178. disabled: !movie,
  179. async setPresenceData() {
  180. data.meta.title =
  181. document
  182. .querySelector("div.anime-title")
  183. ?.textContent?.replace(
  184. document.querySelector("div.anime-title > h3")?.textContent ?? "",
  185. ""
  186. ) ?? "";
  187. data.presenceData.smallImageKey = video.paused
  188. ? Assets.Pause
  189. : Assets.Play;
  190. data.presenceData.smallImageText = video.paused
  191. ? (await strings).pause
  192. : (await strings).play;
  193. [data.presenceData.startTimestamp, data.presenceData.endTimestamp] =
  194. presence.getTimestamps(video.currentTime, video.duration);
  195. data.presenceData.buttons = [
  196. {
  197. label: (await strings).buttonViewMovie,
  198. url: href,
  199. },
  200. ];
  201. if (video.paused) {
  202. delete data.presenceData.endTimestamp;
  203. delete data.presenceData.startTimestamp;
  204. }
  205. },
  206. },
  207. "/filme/(dublado|legendado)/([a-zA-Z0-9-]+)": {
  208. disabled: privacy || !movie,
  209. async setPresenceData() {
  210. data.presenceData.details = (await strings).viewMovie;
  211. data.presenceData.state = document.querySelector(
  212. "div.infos_left > div > h2"
  213. )?.textContent;
  214. data.presenceData.buttons = [
  215. {
  216. label: (await strings).buttonViewMovie,
  217. url: href,
  218. },
  219. ];
  220. },
  221. },
  222. "/minha-conta": {
  223. disabled: privacy,
  224. async setPresenceData() {
  225. data.presenceData.details = (await strings).viewAccount;
  226. },
  227. },
  228. "/pesquisa": {
  229. async setPresenceData() {
  230. data.presenceData.details = (await strings).searchFor;
  231. data.presenceData.state = new URLSearchParams(search).get("titulo");
  232. },
  233. },
  234. };
  235. data.settings = [
  236. {
  237. id: "timestamp",
  238. delete: true,
  239. uses: ["startTimestamp", "endTimestamp"],
  240. },
  241. {
  242. id: "buttons",
  243. delete: true,
  244. uses: ["buttons"],
  245. },
  246. {
  247. id: "privacy",
  248. delete: true,
  249. value: true,
  250. uses: ["buttons"],
  251. },
  252. {
  253. presence: [
  254. {
  255. page: "/pesquisa",
  256. uses: "state",
  257. if: {
  258. k: privacy,
  259. delete: true,
  260. },
  261. },
  262. {
  263. page: "/pesquisa",
  264. uses: "details",
  265. if: {
  266. k: privacy,
  267. v: (await strings).searchSomething,
  268. },
  269. },
  270. {
  271. page: "/anime/(dublado|legendado)/([a-zA-Z0-9-]+)/([a-z-0-9]+)",
  272. uses: "details",
  273. setTo: await presence.getSetting<string>("AnimeDetails"),
  274. if: [
  275. {
  276. k: privacy && anime,
  277. v: (await strings).watchSeries,
  278. },
  279. {
  280. k: !anime,
  281. delete: true,
  282. },
  283. ],
  284. replace: [
  285. {
  286. input: "%title%",
  287. output: data.meta.title,
  288. },
  289. {
  290. input: "%episode%",
  291. output: data.meta.episode,
  292. },
  293. ],
  294. },
  295. {
  296. page: "/anime/(dublado|legendado)/([a-zA-Z0-9-]+)/([a-z-0-9]+)",
  297. uses: "state",
  298. setTo: AnimeState,
  299. if: {
  300. k: !anime || privacy || AnimeState.includes("{0}"),
  301. delete: true,
  302. },
  303. replace: [
  304. {
  305. input: "%title%",
  306. output: data.meta.title,
  307. },
  308. {
  309. input: "%episode%",
  310. output: data.meta.episode,
  311. },
  312. ],
  313. },
  314. {
  315. page: "/filme/(dublado|legendado)/([a-zA-Z0-9-]+)/([a-z-0-9]+)",
  316. uses: "details",
  317. setTo: await presence.getSetting<string>("MovieDetails"),
  318. if: [
  319. {
  320. k: privacy && movie,
  321. v: (await strings).watchMovie,
  322. },
  323. {
  324. k: !movie,
  325. delete: true,
  326. },
  327. ],
  328. replace: [
  329. {
  330. input: "%title%",
  331. output: data.meta.title,
  332. },
  333. ],
  334. },
  335. {
  336. page: "/filme/(dublado|legendado)/([a-zA-Z0-9-]+)/([a-z-0-9]+)",
  337. uses: "state",
  338. setTo: MovieState,
  339. if: {
  340. k: !movie || privacy || MovieState.includes("{0}"),
  341. delete: true,
  342. },
  343. replace: [
  344. {
  345. input: "%title%",
  346. output: data.meta.title,
  347. },
  348. ],
  349. },
  350. ],
  351. },
  352. ];
  353. for (const [k, v] of Object.entries(data.presence)) {
  354. if (pathname.match(k) && !v.disabled) {
  355. v.setPresenceData();
  356. break;
  357. }
  358. }
  359. for (const setting of data.settings) {
  360. const settingValue = await presence
  361. .getSetting<boolean>(setting.id)
  362. .catch(() => null);
  363. if (
  364. ((!settingValue && !setting.value) || settingValue === setting.value) &&
  365. setting.delete &&
  366. !setting.presence
  367. ) {
  368. for (const PData of setting.uses)
  369. delete data.presenceData[PData as keyof PresenceData];
  370. } else if (setting.presence) {
  371. for (const presenceSetting of setting.presence) {
  372. if (pathname.match(presenceSetting.page)) {
  373. if (presenceSetting.setTo && !presenceSetting.replace) {
  374. data.presenceData[presenceSetting.uses as "details"] =
  375. presenceSetting.setTo;
  376. } else if (presenceSetting.setTo && presenceSetting.replace) {
  377. let replaced = presenceSetting.setTo;
  378. for (const toReplace of presenceSetting.replace)
  379. replaced = replaced.replace(toReplace.input, toReplace.output);
  380. if (replaced)
  381. data.presenceData[presenceSetting.uses as "details"] = replaced;
  382. }
  383. if (presenceSetting.if) {
  384. if (Array.isArray(presenceSetting.if)) {
  385. for (const setting of presenceSetting.if) {
  386. if (setting.k) {
  387. if (setting.delete && !setting.v)
  388. delete data.presenceData[presenceSetting.uses];
  389. else if (setting.v) {
  390. data.presenceData[presenceSetting.uses as "details"] =
  391. setting.v;
  392. }
  393. }
  394. }
  395. } else if (presenceSetting.if.k) {
  396. if (presenceSetting.if.delete && !presenceSetting.if.v)
  397. delete data.presenceData[presenceSetting.uses];
  398. else if (presenceSetting.if.v) {
  399. data.presenceData[presenceSetting.uses as "details"] =
  400. presenceSetting.if.v;
  401. }
  402. }
  403. }
  404. }
  405. }
  406. }
  407. }
  408. for (const x of ["state", "details"]) {
  409. if (data.presenceData[x as "details"] === "undefined")
  410. delete data.presenceData[x as "details"];
  411. }
  412. if (!data.presenceData.details) presence.setActivity();
  413. else presence.setActivity(data.presenceData);
  414. });