Save Leetcode Problem to Obsidian

Save the current leetcode problem as new obsidian note.

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
  1. // ==UserScript==
  2. // @name Save Leetcode Problem to Obsidian
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-07-26
  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_openInTab
  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. linkText = linkText.replace(/\?/g, "");
  81. console.log('Link Text:', linkText);
  82. return linkText;
  83. } else {
  84. console.log('Link element not found for href:', matchedPath);
  85. return null;
  86. }
  87. }
  88.  
  89. // Call the function to capture and log the link text
  90. let problemName = getProblemName();
  91. let today = new Date().toISOString().split('T')[0];
  92.  
  93. let content = `---
  94. lc-link: ${currentUrl}
  95. minutes-spent: ${minutesSpent}
  96. attempts:
  97. difficulty: ${difficultyValue}
  98. solved-on: ${today}
  99. comment:
  100. review-on:
  101. ---
  102. # Algorithm
  103.  
  104.  
  105. # Complexities
  106.  
  107. - Time:
  108. - Space:
  109.  
  110. # Alternative Approach
  111.  
  112.  
  113. # New Functions`;
  114.  
  115.  
  116. async function createObsidianDocument(obsidianUrl, documentPath, content, token) {
  117. // check if exists
  118. let fileExists = false;
  119. try {
  120. let response = await fetch(obsidianUrl + '/vault' + documentPath, {
  121. method: 'GET',
  122. headers: {
  123. 'Content-Type': 'text/markdown',
  124. 'Authorization': `Bearer ${token}`
  125. }
  126. });
  127. if (response.ok) {
  128. fileExists = true;
  129. }
  130. } catch (error) {
  131. console.error('Error:', error);
  132. }
  133.  
  134. // not exist, create it
  135. if (!fileExists) {
  136. try {
  137. let response = await fetch(obsidianUrl + '/vault' + documentPath, {
  138. method: 'PUT',
  139. headers: {
  140. 'Content-Type': 'text/markdown',
  141. 'Authorization': `Bearer ${token}`
  142. },
  143. body: content
  144. });
  145.  
  146. if (response.ok) {
  147. console.log('Document created successfully:', response.statusText);
  148. } else {
  149. console.error('Error creating document:', response.statusText);
  150. }
  151. } catch (error) {
  152. console.error('Error:', error);
  153. }
  154. }
  155.  
  156. function openObsidianLink() {
  157. GM_openInTab('obsidian://', { active: true, insert: true });
  158. }
  159.  
  160. // open the file in obsidian
  161. try {
  162. let response = await fetch(obsidianUrl + '/open' + documentPath, {
  163. method: 'POST',
  164. headers: {
  165. 'Authorization': `Bearer ${token}`
  166. }
  167. });
  168. if (!fileExists) {
  169. GM_notification({ title: 'Problem Imported Successfully', text: `Please open your Obsidian editor to see it.`, timeout: 5000, onclick: openObsidianLink });
  170. } else {
  171. GM_notification({ title: 'Problem Exists!', text: `Please open your Obsidian editor to see it.`, timeout: 5000, onclick: openObsidianLink });
  172. }
  173. } catch (error) {
  174. console.error('Error:', error);
  175. GM_notification({ title: 'Error Occurred', text: `${error} Do you have Obsidian opened?`, timeout: 5000 });
  176. }
  177. }
  178.  
  179. // Function to get the bearer token
  180. function getOrPromptForKey(key, promptText) {
  181. let token = GM_getValue(key, null);
  182. if (!token) {
  183. token = prompt(promptText);
  184. if (token) {
  185. GM_setValue(key, token);
  186. GM_notification({ title: 'Input Saved', text: `Your input for ${key} has been saved.`, timeout: 5000 });
  187. }
  188. }
  189. return token;
  190. }
  191.  
  192. function saveToObsidian() {
  193. let token = getOrPromptForKey('obsidian_web_api_key', 'Please enter your Obsidian API key:');
  194. let obsidianUrl = getOrPromptForKey('obsidian_api_url', 'Please enter the obsidian HTTPS API URL').replace(/\/$/, '');
  195. let documentPath = `/Algorithms/LeetCode/${problemName}.md`
  196.  
  197. if (token) {
  198. // Call the function to create the Obsidian document
  199. createObsidianDocument(obsidianUrl, documentPath, content, token);
  200. } else {
  201. console.error('Bearer token is not available.');
  202. }
  203. }
  204.  
  205. saveToObsidian();
  206.  
  207. })();