Save Leetcode Problem to Obsidian

Save the current leetcode problem as new obsidian note.

As of 2024-07-17. See the latest version.

  1. // ==UserScript==
  2. // @name Save Leetcode Problem to Obsidian
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-07-17
  5. // @description Save the current leetcode problem as new obsidian note.
  6. // @author miscde
  7. // @match https://leetcode.com/problems/*
  8. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_notification
  13. // @grant GM_addStyle
  14. // @run-at context-menu
  15. // @license MIT
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. // Function to find and return the timer value
  22. let containerElement = document.querySelector('#ide-top-btns');
  23.  
  24. function getTimerValue() {
  25. if (containerElement) {
  26. // Find the timer element within the container
  27. let timerElement = containerElement.querySelector('.select-none.pr-2.text-sm.text-text-secondary.dark\\:text-text-secondary');
  28.  
  29. if (timerElement) {
  30. let timerValue = timerElement.textContent || timerElement.innerText;
  31. console.log('Current Timer Value:', timerValue);
  32. return timerValue;
  33. } else {
  34. console.log('Timer element not found within the container');
  35. return null;
  36. }
  37. } else {
  38. console.log('Container element not found');
  39. return null;
  40. }
  41. }
  42.  
  43. // Call the function to capture and log the timer value
  44. let timerValue = getTimerValue();
  45. let [hours, minutes, seconds] = timerValue.split(':').map(Number);
  46. let minutesSpent = hours * 60 + minutes + Math.round(seconds / 60);
  47.  
  48. let currentUrl = window.location.href;
  49.  
  50. let difficultyElement = document.querySelector('[class*="text-difficulty-"]');
  51.  
  52. // Function to get the difficulty level
  53. function getDifficulty() {
  54. if (difficultyElement) {
  55. let difficultyText = difficultyElement.textContent || difficultyElement.innerText;
  56. difficultyText = difficultyText.toLowerCase();
  57. console.log('Problem Difficulty:', difficultyText);
  58. return difficultyText;
  59. } else {
  60. console.log('Difficulty element not found');
  61. return null;
  62. }
  63. }
  64.  
  65. // Call the function to capture and log the difficulty
  66. let difficultyValue = getDifficulty();
  67.  
  68. let fullPath = window.location.pathname;
  69.  
  70. // Extract the relevant part (/problems/xxxx)
  71. let matchedPath = fullPath.match(/\/problems\/[^\/]+/)[0];
  72.  
  73. // Find the <a> element with the matching href attribute
  74. let linkElement = document.querySelector(`a[href*="${matchedPath}"]`);
  75.  
  76. // Function to get the text of the <a> element
  77. function getProblemName() {
  78. if (linkElement) {
  79. let linkText = linkElement.textContent || linkElement.innerText;
  80. console.log('Link Text:', linkText);
  81. return linkText;
  82. } else {
  83. console.log('Link element not found for href:', matchedPath);
  84. return null;
  85. }
  86. }
  87.  
  88. // Call the function to capture and log the link text
  89. let problemName = getProblemName();
  90. let today = new Date().toISOString().split('T')[0];
  91.  
  92. let content = `---
  93. lc-link: ${currentUrl}
  94. minutes-spent: ${minutesSpent}
  95. attempts:
  96. difficulty: ${difficultyValue}
  97. solved-on: ${today}
  98. comment:
  99. review-on:
  100. ---
  101. # Algorithm
  102.  
  103.  
  104. # Complexities
  105.  
  106. - Time:
  107. - Space:
  108.  
  109. # Alternative Approach
  110.  
  111.  
  112. # New Functions`;
  113.  
  114.  
  115. async function createObsidianDocument(obsidianUrl, documentPath, content, token) {
  116. // check if exists
  117. let fileExists = false;
  118. try {
  119. let response = await fetch(obsidianUrl + '/vault' + documentPath, {
  120. method: 'GET',
  121. headers: {
  122. 'Content-Type': 'text/markdown',
  123. 'Authorization': `Bearer ${token}`
  124. }
  125. });
  126. if (response.ok) {
  127. fileExists = true;
  128. }
  129. } catch (error) {
  130. console.error('Error:', error);
  131. }
  132.  
  133. // not exist, create it
  134. if (!fileExists) {
  135. try {
  136. let response = await fetch(obsidianUrl + '/vault' + documentPath, {
  137. method: 'PUT',
  138. headers: {
  139. 'Content-Type': 'text/markdown',
  140. 'Authorization': `Bearer ${token}`
  141. },
  142. body: content
  143. });
  144.  
  145. if (response.ok) {
  146. console.log('Document created successfully:', response.statusText);
  147. } else {
  148. console.error('Error creating document:', response.statusText);
  149. }
  150. } catch (error) {
  151. console.error('Error:', error);
  152. }
  153. }
  154.  
  155. // open the file in obsidian
  156. try {
  157. let response = await fetch(obsidianUrl + '/open' + documentPath, {
  158. method: 'POST',
  159. headers: {
  160. 'Authorization': `Bearer ${token}`
  161. }
  162. });
  163. } catch (error) {
  164. console.error('Error:', error);
  165. }
  166. }
  167.  
  168. // Function to get the bearer token
  169. function getOrPromptForKey(key, promptText) {
  170. let token = GM_getValue(key, null);
  171. if (!token) {
  172. token = prompt(promptText);
  173. if (token) {
  174. GM_setValue(key, token);
  175. GM_notification({ title: 'Input Saved', text: `Your input for ${key} has been saved.`, timeout: 3000 });
  176. }
  177. }
  178. return token;
  179. }
  180.  
  181. function saveToObsidian() {
  182. let token = getOrPromptForKey('obsidian_web_api_key', 'Please enter your Obsidian API key:');
  183. let obsidianUrl = getOrPromptForKey('obsidian_api_url', 'Please enter the obsidian HTTPS API URL').replace(/\/$/, '');
  184. let documentPath = `/Algorithms/LeetCode/${problemName}.md`
  185.  
  186. if (token) {
  187. // Call the function to create the Obsidian document
  188. createObsidianDocument(obsidianUrl, documentPath, content, token);
  189. } else {
  190. console.error('Bearer token is not available.');
  191. }
  192. }
  193.  
  194. saveToObsidian();
  195.  
  196. })();