presence.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. const presence = new Presence({
  2. clientId: "785263902321541181", //Presence Application ID on Discord Developers.
  3. }),
  4. browsingTimestamp = Math.floor(Date.now() / 1000);
  5. function unescapeHTML(string: string): string {
  6. const textarea = document.createElement("textarea");
  7. textarea.textContent = string;
  8. return textarea.textContent;
  9. }
  10. presence.on("UpdateData", async () => {
  11. const presenceData: PresenceData = {
  12. largeImageKey:
  13. "https://cdn.rcd.gg/PreMiD/websites/L/LinkedIn/assets/logo.png",
  14. startTimestamp: browsingTimestamp,
  15. },
  16. path = document.location.pathname;
  17. if (document.location.hostname === "www.linkedin.com") {
  18. //Homepage.
  19. if (path === "/feed/") presenceData.details = "Browsing Feed.";
  20. else if (path.includes("/feed/hashtag/")) {
  21. //Feed hashtag subsection.
  22. presenceData.details = "Browsing Feed:";
  23. presenceData.state = `#${unescapeHTML(
  24. document
  25. .querySelector(
  26. "div.application-outlet > div.authentication-outlet > div > div > div > div > section > div > div:first-child > div > h3 > span > span:last-child"
  27. )
  28. .textContent.trim()
  29. )}.`;
  30. } else if (path.includes("/feed/")) {
  31. //Feed follow subsections.
  32. enum feedSubSection {
  33. "follow/" = "Browsing suggestions.",
  34. "following/" = "Viewing Following:",
  35. "followers/" = "Viewing Followers.",
  36. }
  37. enum filterType {
  38. connection = "Connections",
  39. member = "Members",
  40. company = "Companies",
  41. channel = "Hashtags",
  42. }
  43. const subSection =
  44. feedSubSection[
  45. path.split("/feed/").pop() as keyof typeof feedSubSection
  46. ];
  47. presenceData.details = subSection;
  48. //If the user is on following/ subsection, show the selected filter.
  49. if (subSection === feedSubSection["following/"]) {
  50. presenceData.state = `Filtering by ${
  51. filterType[
  52. document.location.search
  53. .split("?filterType=")
  54. .pop()
  55. .split("&")
  56. .shift() as keyof typeof filterType
  57. ] || "All"
  58. }.`;
  59. }
  60. } else if (path.includes("/mynetwork/")) {
  61. //My Network section & subsections.
  62. presenceData.details = "Managing Network:";
  63. //Invitations subsection.
  64. if (path.includes("/invitation-manager/"))
  65. presenceData.state = "Viewing Invitations.";
  66. //Contacts subsections.
  67. else if (
  68. path.includes("/import-contacts/") ||
  69. path === "/mynetwork/contacts/"
  70. ) {
  71. //Contacts homepage.
  72. if (path === "/mynetwork/contacts/")
  73. presenceData.state = "Browsing Contacts.";
  74. //Saved contacts.
  75. else if (path.endsWith("saved-contacts/"))
  76. presenceData.state = "Browsing Saved contacts.";
  77. //Adding contacts.
  78. else presenceData.state = "Adding Contacts.";
  79. } else if (path.includes("/colleagues/"))
  80. //Teammates subsection.
  81. presenceData.state = "Browsing Colleagues.";
  82. //My Network subsections with same link path structure.
  83. else {
  84. enum networkSubSection {
  85. "connections/" = "Browsing Connections.",
  86. "events/" = "Browsing Events.",
  87. "newsletters/" = "Reading Newsletters.",
  88. }
  89. presenceData.state =
  90. networkSubSection[
  91. path
  92. .split(/\/[a-z]+-[a-z]+\//)
  93. .pop() as keyof typeof networkSubSection
  94. ] || "Homepage.";
  95. }
  96. } else if (path.includes("/jobs/") || path === "/my-items/saved-jobs/") {
  97. //Jobs section.
  98. //Application settings subsection.
  99. if (path.endsWith("application-settings/")) {
  100. presenceData.details = "Editing settings:";
  101. presenceData.state = "Application.";
  102. } else {
  103. //Others subsections.
  104. presenceData.details = "Browsing Jobs:";
  105. //Saved Jobs subsection.
  106. if (path === "/my-items/saved-jobs/")
  107. presenceData.state = "Saved Jobs.";
  108. //Searching for a Job subsection.
  109. else if (path === "/jobs/search/") {
  110. //Getting user preference for showJobsQuery.
  111. const showJobsQuery = await presence.getSetting<boolean>(
  112. "showJobsQuery"
  113. );
  114. if (showJobsQuery) {
  115. presenceData.state = `Searching for a "${decodeURI(
  116. document.location.search
  117. .split("keywords=")
  118. .pop()
  119. .split("&")
  120. .shift()
  121. )}" position.`;
  122. } else presenceData.state = "Searching for a job.";
  123. } else presenceData.state = "Homepage.";
  124. //Homepage.
  125. }
  126. } else if (path.includes("/interview-prep/")) {
  127. //Interview prep section (Jobs related section with a different path).
  128. presenceData.details = "Taking an Interview Prep:";
  129. presenceData.state = `${unescapeHTML(
  130. document
  131. .querySelector(
  132. "div.application-outlet > div.authentication-outlet > main > div > section > section > header > div > div:first-child > h2"
  133. )
  134. .textContent.trim()
  135. )}.`;
  136. } else if (path.includes("/messaging/")) {
  137. //Messaging section.
  138. presenceData.details = "Messaging:";
  139. //New message subsection.
  140. if (path === "/messaging/thread/new/")
  141. presenceData.state = "Writing a new message.";
  142. //New group subsection.
  143. else if (path === "/messaging/compose-group/")
  144. presenceData.state = "Creating a new group.";
  145. //Chats subsection.
  146. else {
  147. //Getting user preference for showChatUsername.
  148. const showChatUsername = await presence.getSetting<boolean>(
  149. "showChatUsername"
  150. );
  151. if (showChatUsername) {
  152. presenceData.state = `Chatting with ${unescapeHTML(
  153. document
  154. .querySelector(
  155. "div.application-outlet > div.authentication-outlet > #messaging > div > div > div:nth-child(2) > div:first-child > div > a > div > div > dl > dt > #thread-detail-jump-target"
  156. )
  157. .textContent.trim()
  158. )}.`;
  159. } else presenceData.state = "Chatting with someone.";
  160. }
  161. } else if (path === "/notifications/") {
  162. //Notifications section.
  163. presenceData.details = "Viewing Notifications.";
  164. } else if (path.match(/\/in\/[A-Za-z0-9-]+\/$/)) {
  165. //Profile page section.
  166. presenceData.details = "Viewing a profile:";
  167. presenceData.state = `${document
  168. .querySelector(
  169. "div.application-outlet > div.authentication-outlet > #profile-content > div > div > div > div:nth-child(2) > main > div > section > div:nth-child(2) > div:nth-child(2) > div:first-child > ul:first-child > li:first-child"
  170. )
  171. .textContent.trim()}.`;
  172. } else if (path.match(/\/in\/[A-Za-z0-9-]+\//)) {
  173. //Profile detail subsection.
  174. if (path.includes("/detail/")) {
  175. enum detailSubSection {
  176. "recent-activity" = "Activities",
  177. skills = "Skills",
  178. interests = "Interests",
  179. "contact-info" = "Contact Info",
  180. }
  181. //If the user is editing skills (the only edit related subsection with "detail" path).
  182. if (path === "/in/luca-biagetti/detail/skills/add/") {
  183. presenceData.details = "Editing profile:";
  184. presenceData.state = "Skills.";
  185. } else {
  186. //Actually detail subsections.
  187. presenceData.details = "Viewing user details:";
  188. presenceData.state = `${unescapeHTML(
  189. path !== "/in/luca-biagetti/detail/recent-activity/"
  190. ? document
  191. .querySelector(
  192. "div.application-outlet > div.authentication-outlet > #profile-content > div > div > div > div:nth-child(2) > main > div > section > div:nth-child(2) > div:nth-child(2) > div:first-child > ul:first-child > li:first-child"
  193. )
  194. .textContent.trim()
  195. : document
  196. .querySelector(
  197. "div.application-outlet > div.authentication-outlet > #profile-content > div > div > div > div > div:first-child > header > h1"
  198. )
  199. .textContent.trim()
  200. .replace("’s Activity", "")
  201. )}'s ${
  202. detailSubSection[
  203. path
  204. .split(/\/in\/[A-Za-z0-9-]+\/detail\//)
  205. .pop()
  206. .split("/")
  207. .shift() as keyof typeof detailSubSection as keyof typeof detailSubSection
  208. ]
  209. }.`;
  210. }
  211. } else if (path.includes("/edit/")) {
  212. //Profile edit subsection.
  213. enum editSubSection {
  214. intro = "Intro.",
  215. about = "About.",
  216. "add-feed-post" = "Posts.",
  217. "add-article" = "Articles.",
  218. "add-link" = "Links.",
  219. position = "Experiences.",
  220. education = "Education.",
  221. certification = "Certifications.",
  222. "volunteer-experience" = "Volunteer experiences.",
  223. publication = "Publications.",
  224. patent = "Patents.",
  225. course = "Courses.",
  226. project = "Projects.",
  227. honor = "Honors & Awards.",
  228. "test-score" = "Test scores.",
  229. language = "Languages.",
  230. organization = "Organizations.",
  231. "secondary-language" = "Secondary language.",
  232. "contact-info" = "Contact info.",
  233. }
  234. presenceData.details = "Editing profile:";
  235. presenceData.state =
  236. editSubSection[
  237. path
  238. .split(/\/in\/[A-Za-z0-9-]+\/edit\//)
  239. .pop()
  240. .replace("forms/", "")
  241. .split("/")
  242. .shift() as keyof typeof editSubSection
  243. ];
  244. }
  245. } else if (path.match(/\/company\/[A-Za-z0-9-]+\//)) {
  246. //Company page section.
  247. presenceData.details = "Viewing a company:";
  248. presenceData.state = `${unescapeHTML(
  249. document
  250. .querySelector(
  251. "div.application-outlet > div.authentication-outlet > div > div:nth-child(3) > div:first-child > section > div > div > div:nth-child(2) > div:first-child > div:first-child > div:nth-child(2) > div > h1 > span"
  252. )
  253. .textContent.trim()
  254. )}.`;
  255. } else if (path.match(/\/school\/[A-Za-z0-9-]+\//)) {
  256. //School page section.
  257. presenceData.details = "Viewing a school:";
  258. presenceData.state = `${unescapeHTML(
  259. document
  260. .querySelector(
  261. "div.application-outlet > div.authentication-outlet > div > div:nth-child(3) > div:first-child > section > div > div > div:nth-child(2) > div:first-child > div:first-child > div:nth-child(2) > div > h1 > span"
  262. )
  263. .textContent.trim()
  264. )}.`;
  265. } else if (path.startsWith("/groups/")) {
  266. //Groups section.
  267. //Group page subsection.
  268. if (path.match(/\/groups\/[0-9]+\//)) {
  269. presenceData.details = "Viewing a group:";
  270. presenceData.state = `${unescapeHTML(
  271. document
  272. .querySelector(
  273. "div.application-outlet > div.authentication-outlet > div > div:nth-child(2) > main > div:first-child > section > div > h1 > span"
  274. )
  275. .textContent.trim()
  276. .replaceAll("<!---->", "")
  277. )}.`;
  278. } else {
  279. presenceData.details = "Browsing Groups:";
  280. //Requested groups subsection.
  281. if (path === "/groups/requests/")
  282. presenceData.state = "Requested groups.";
  283. //Homepage.
  284. else presenceData.state = "My groups.";
  285. }
  286. } else if (path.includes("/psettings/")) {
  287. //Settings section.
  288. presenceData.details = "Editing settings.";
  289. } else if (path === "/my-items/") {
  290. //My Items section.
  291. presenceData.details = "Browsing My Items.";
  292. } else if (path === "/post/new/") {
  293. //New Post section.
  294. presenceData.details = "Writing a New Post.";
  295. } else if (path.includes("/search/results/")) {
  296. //Searching for something section.
  297. presenceData.details = "Searching for something.";
  298. } else {
  299. //Others sections in "Work" category not supported atm.
  300. presenceData.details = "Doing stuffs.";
  301. }
  302. }
  303. if (presenceData.details) presence.setActivity(presenceData);
  304. else presence.setActivity();
  305. });