presence.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. const presence = new Presence({
  2. clientId: '1050466196220289104',
  3. })
  4. const browsingTimestamp = Math.floor(Date.now() / 1000)
  5. presence.on('UpdateData', async () => {
  6. const presenceData: PresenceData = {
  7. largeImageKey: 'https://cdn.rcd.gg/PreMiD/websites/A/AnkiWeb/assets/logo.png',
  8. startTimestamp: browsingTimestamp,
  9. }
  10. const { pathname, hostname, href } = document.location
  11. const pathList = pathname.split('/').filter(x => x)
  12. switch (hostname) {
  13. case 'ankiweb.net': {
  14. switch (pathList[0]) {
  15. case 'account': {
  16. switch (pathList[1]) {
  17. case 'login':
  18. case 'register': {
  19. presenceData.details = 'Logging in'
  20. break
  21. }
  22. case 'terms': {
  23. presenceData.details = 'Reading the terms of service'
  24. break
  25. }
  26. case 'privacy': {
  27. presenceData.details = 'Reading the privacy policy'
  28. break
  29. }
  30. default: {
  31. presenceData.details = 'Managing account settings'
  32. }
  33. }
  34. break
  35. }
  36. case 'decks': {
  37. switch (pathList[1] ?? '') {
  38. case '': {
  39. presenceData.details = 'Browsing decks'
  40. const [newItems, dueItems] = [
  41. ...document.querySelectorAll('.deckDueNumber'),
  42. ].reduce(
  43. (current, item, index) => {
  44. if (index % 2)
  45. current[0] += +item.textContent!
  46. else current[1] += +item.textContent!
  47. return current
  48. },
  49. [0, 0],
  50. )
  51. presenceData.state = `${dueItems} due, ${newItems} new`
  52. break
  53. }
  54. case 'share': {
  55. presenceData.details = 'Sharing a deck'
  56. break
  57. }
  58. }
  59. break
  60. }
  61. case 'search': {
  62. presenceData.details = 'Searching for cards'
  63. presenceData.state = document.querySelector('small')?.textContent
  64. break
  65. }
  66. case 'shared': {
  67. switch (pathList[1]) {
  68. case 'decks': {
  69. if (pathList[2]) {
  70. presenceData.details = 'Viewing shared decks by term'
  71. presenceData.state = document.querySelector('h1')?.textContent
  72. }
  73. break
  74. }
  75. case 'info': {
  76. presenceData.details = 'Viewing a shared deck'
  77. presenceData.state = document.querySelector('h1')?.textContent
  78. presenceData.buttons = [{ label: 'View Deck', url: href }]
  79. break
  80. }
  81. case 'mine': {
  82. presenceData.details = 'Viewing their shared decks'
  83. break
  84. }
  85. }
  86. break
  87. }
  88. }
  89. break
  90. }
  91. case 'ankiuser.net': {
  92. switch (pathList[0]) {
  93. case 'study': {
  94. switch (pathList[1] ?? '') {
  95. case '': {
  96. presenceData.details = 'Studying'
  97. presenceData.state = `${document
  98. .querySelector<HTMLSpanElement>('#rightStudyMenu')
  99. ?.textContent
  100. ?.match(/\d+/g)
  101. ?.reduce(
  102. (current, item) => current + +item,
  103. 0,
  104. ) ?? 0} cards remaining`
  105. break
  106. }
  107. case 'finished': {
  108. presenceData.details = 'Finished studying'
  109. break
  110. }
  111. case 'options': {
  112. presenceData.details = 'Changing study options'
  113. break
  114. }
  115. }
  116. break
  117. }
  118. case 'edit': {
  119. if (pathList[1])
  120. presenceData.details = 'Editing a card'
  121. else presenceData.details = 'Creating a card'
  122. break
  123. }
  124. }
  125. break
  126. }
  127. case 'apps.ankiweb.net': {
  128. presenceData.details = 'Downloading Anki'
  129. break
  130. }
  131. case 'changes.ankiweb.net': {
  132. presenceData.details = 'Browsing the changelog'
  133. presenceData.state = document.querySelector('h1')?.textContent
  134. break
  135. }
  136. case 'docs.ankiweb.net': {
  137. presenceData.details = 'Reading the docs'
  138. presenceData.state = document.querySelector<HTMLAnchorElement>('.header')?.textContent
  139. break
  140. }
  141. case 'faqs.ankiweb.net': {
  142. presenceData.details = 'Reading the FAQs'
  143. presenceData.state = document.querySelector('h1')?.textContent
  144. break
  145. }
  146. case 'forums.ankiweb.net': {
  147. switch (pathList[0] ?? '') {
  148. case 'badges': {
  149. presenceData.details = 'Browsing badges'
  150. break
  151. }
  152. case 'c': {
  153. presenceData.details = 'Browsing a category'
  154. presenceData.state = document.querySelector<HTMLSpanElement>('.category-name')?.textContent
  155. break
  156. }
  157. case 'cakeday': {
  158. presenceData.details = `Browsing ${
  159. document.querySelector<HTMLHeadingElement>('.cakeday-header')
  160. ?.textContent
  161. }`
  162. break
  163. }
  164. case 'g': {
  165. if (pathList[1]) {
  166. presenceData.details = 'Viewing a group'
  167. presenceData.state = document.querySelector<HTMLSpanElement>(
  168. '.group-info-name',
  169. )?.textContent
  170. }
  171. else {
  172. presenceData.details = 'Browsing groups'
  173. }
  174. break
  175. }
  176. case '':
  177. case 'latest':
  178. case 'new': {
  179. presenceData.details = 'Browsing the latest posts'
  180. break
  181. }
  182. case 'unread': {
  183. presenceData.details = 'Browsing unread posts'
  184. break
  185. }
  186. case 't': {
  187. presenceData.details = 'Browsing a topic'
  188. presenceData.state = document
  189. .querySelector<HTMLSpanElement>('.fancy-title')
  190. ?.textContent
  191. ?.trim()
  192. presenceData.buttons = [{ label: 'View Topic', url: href }]
  193. break
  194. }
  195. case 'top': {
  196. presenceData.details = 'Browsing top posts'
  197. break
  198. }
  199. case 'u': {
  200. if (pathList[1]) {
  201. presenceData.details = 'Viewing a user\'s profile'
  202. presenceData.state = document.querySelector<HTMLSpanElement>(
  203. '.user-profile-names > .username',
  204. )?.textContent
  205. presenceData.smallImageKey = document.querySelector<HTMLImageElement>(
  206. '.user-profile-avatar > img',
  207. )?.src
  208. }
  209. else {
  210. presenceData.details = 'Browsing users'
  211. }
  212. break
  213. }
  214. default: {
  215. presenceData.details = 'Browsing the forums'
  216. }
  217. }
  218. break
  219. }
  220. }
  221. if (presenceData.details)
  222. presence.setActivity(presenceData)
  223. else presence.setActivity()
  224. })