leetcode2notion

Save LeetCode problems to Notion after clicking a button.

Versione datata 08/09/2024. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         leetcode2notion
// @namespace    wuyifff
// @version      1.0
// @description  Save LeetCode problems to Notion after clicking a button.
// @author       wuyifff
// @match        https://leetcode.cn/problems/*
// @match        https://leetcode.com/problems/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=leetcode.com
// @grant        GM_xmlhttpRequest
// @license      MIT
// @homepage     https://github.com/wuyifff/leetcode2notion
// ==/UserScript==

(function() {
    'use strict';
    // replace to your own token and ID
    const notionToken = '';
    const databaseId = '';

    // 1. add save button
    // select language button (optional)
    function addUIElements() {
        const button = document.createElement("button");
        button.innerHTML = "Save to Notion";
        button.style.position = "fixed";
        button.style.bottom = "10px";
        button.style.right = "10px";
        button.style.zIndex = 1000;
        button.style.padding = "10px 20px";
        button.style.backgroundColor = "#4CAF50";
        button.style.color = "white";
        button.style.border = "none";
        button.style.borderRadius = "5px";
        button.style.cursor = "pointer";
        button.onclick = saveProblemToNotion;

        const select = document.createElement("select");
        select.id = "languageSelect";
        select.style.position = "fixed";
        select.style.bottom = "50px";
        select.style.right = "10px";
        select.style.zIndex = 1000;
        select.style.padding = "10px";
        select.style.backgroundColor = "#4CAF50";
        select.style.color = "white";
        select.style.border = "none";
        select.style.borderRadius = "5px";
        select.style.cursor = "pointer";

        const optionPython = document.createElement("option");
        optionPython.value = "python";
        optionPython.innerText = "Python";

        const optionCpp = document.createElement("option");
        optionCpp.value = "cpp";
        optionCpp.innerText = "C++";

        select.appendChild(optionPython);
        select.appendChild(optionCpp);

        const container = document.createElement("div");
        container.style.display = "flex";
        container.style.flexDirection = "column";
        container.style.alignItems = "center";
        container.style.marginLeft = "10px";
        //container.appendChild(select);
        container.appendChild(button);

        container.style.position = "fixed";
        container.style.bottom = "10px";
        container.style.right = "10px";
        document.body.appendChild(container);
    }

    // 2. get leetcode problem info
    function getProblemData() {
        const title = document.querySelector('.text-title-large a')?.innerText || 'No title found';
        const difficultyElement = document.querySelector("div[class*='text-difficulty-']");
        const difficulty = difficultyElement ? difficultyElement.innerText : 'No difficulty found';
        const url = window.location.href;
        const tagElements = document.querySelectorAll("a[href*='/tag/']");
        const tagTexts = Array.from(tagElements).map(element => element.innerText);

        const codeDiv = document.querySelector('.view-lines.monaco-mouse-cursor-text[role="presentation"]');
        let codeText = '';
        if (codeDiv) {
            const codeLines = codeDiv.querySelectorAll('div');
            codeText = Array.from(codeLines).map(line => line.innerText).join('\n');
        } else {
            codeText = 'No code found';
        }
        //console.log(codeText);
        //const selectedLanguage = document.getElementById("languageSelect").value;
        const selectedLanguage = 'python';
        return {
            title: title,
            difficulty: difficulty,
            url: url,
            tag: tagTexts,
            code: codeText,
            language: selectedLanguage
        };
    }

    // 3. save to notion and check if duplicate
    async function saveProblemToNotion() {
        const problemData = getProblemData();
        console.log(problemData);

        const searchUrl = `https://api.notion.com/v1/search`;
        const searchBody = {
            "query": problemData.title,
            "filter": {
                "value": "page",
                "property": "object"
            },
            "sort": {
                "direction": "ascending",
                "timestamp": "last_edited_time"
            }
        };

        GM_xmlhttpRequest({
            method: 'POST',
            url: searchUrl,
            headers: {
                'Authorization': `Bearer ${notionToken}`,
                'Content-Type': 'application/json',
                'Notion-Version': '2022-06-28'
            },
            data: JSON.stringify(searchBody),
            onload: function(searchResponse) {
                if (searchResponse.status === 200) {
                    const searchResult = JSON.parse(searchResponse.responseText);
                    const existingPage = searchResult.results.find(result => result.properties?.Title?.title[0]?.text?.content === problemData.title);

                    if (existingPage) {
                        const existingPageUrl = existingPage.url;
                        alert('Problem already exists in Notion! Opening existing page...');
                        window.open(existingPageUrl, '_blank');
                    } else {
                        createNewNotionPage(problemData);
                    }
                } else {
                    console.error('Error searching Notion database', searchResponse.responseText);
                    alert('Failed to search Notion database. Check the console for details.');
                }
            },
            onerror: function(error) {
                console.error('Error in searching Notion database', error);
                alert('An error occurred while searching Notion database.');
            }
        });
    }

    // 4. create new page
    function createNewNotionPage(problemData) {
        const tags = problemData.tag.map(tag => ({
            name: tag
        }));

        const url = `https://api.notion.com/v1/pages`;
        const body = {
            parent: { database_id: databaseId },
            properties: {
                'Title': {
                    title: [
                        {
                            text: {
                                content: problemData.title
                            }
                        }
                    ]
                },
                'Difficulty': {
                    select: {
                        name: problemData.difficulty
                    }
                },
                'Link': {
                    url: problemData.url
                },
                'Date': {
                    date: {
                        start: new Date().toISOString().split('T')[0] // format YYYY-MM-DD
                    }
                },
                'Tags': {
                    multi_select: tags
                }
            },
            children: [
                {
                    object: 'block',
                    type: 'code',
                    code: {
                        rich_text: [
                            {
                                type: 'text',
                                text: {
                                    content: problemData.code
                                }
                            }
                        ],
                        language: problemData.language
                    }
                }
            ]
        };

        GM_xmlhttpRequest({
            method: 'POST',
            url: url,
            headers: {
                'Authorization': `Bearer ${notionToken}`,
                'Content-Type': 'application/json',
                'Notion-Version': '2022-06-28'
            },
            data: JSON.stringify(body),
            onload: function(response) {
                if (response.status === 200) {
                    const responseData = JSON.parse(response.responseText);
                    const notionPageUrl = responseData.url;
                    alert('Problem saved to Notion!');
                    window.open(notionPageUrl, '_blank');
                } else {
                    console.error('Failed to save to Notion', response.responseText);
                    alert('Failed to save to Notion. Check the console for more details.');
                }
            },
            onerror: function(error) {
                console.error('Error in saving to Notion', error);
                alert('An error occurred while saving to Notion.');
            }
        });
    }

    addUIElements();

})();