leetcode2notion

Save LeetCode problems to Notion after clicking a button.

Verze ze dne 08. 09. 2024. Zobrazit nejnovější verzi.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         leetcode2notion
// @namespace    wuyifff
// @version      1.1
// @description  Save LeetCode problems to Notion after clicking a button.
// @author       wuyifff
// @match        https://leetcode.cn/problems/*
// @match        https://leetcode.com/problems/*
// @connect      api.notion.com
// @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();

})();