Skip to content

Instantly share code, notes, and snippets.

@davehague
Last active May 29, 2024 18:09
Show Gist options
  • Save davehague/bcb6fb078f7fb87ecc8a87490f19c399 to your computer and use it in GitHub Desktop.
Save davehague/bcb6fb078f7fb87ecc8a87490f19c399 to your computer and use it in GitHub Desktop.
Tampermonkey script
// ==UserScript==
// @name         ChatGPT Chat Extractor
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Extract ChatGPT chats to IndexedDB
// @author       Dave Hague
// @match        https://chatgpt.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function initDB() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open('ChatGPTChats', 1);
            request.onupgradeneeded = function(event) {
                const db = event.target.result;
                if (!db.objectStoreNames.contains('links')) {
                    const objectStore = db.createObjectStore('links', { keyPath: 'id', autoIncrement: true });
                    objectStore.createIndex('url', 'url', { unique: true });
                }
            };
            request.onsuccess = function(event) { resolve(event.target.result); };
            request.onerror = function(event) { reject('Database error: ' + event.target.errorCode); };
        });
    }

    function checkURLExists(db, url) {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction(['links'], 'readonly');
            const objectStore = transaction.objectStore('links');
            const index = objectStore.index('url');
            const request = index.get(url);
            request.onsuccess = function(event) {
                resolve(!!event.target.result);
            };
            request.onerror = function(event) {
                reject('Check error: ' + event.target.errorCode);
            };
        });
    }

    function saveDataToDB(db, data) {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction(['links'], 'readwrite');
            const objectStore = transaction.objectStore('links');
            const request = objectStore.add(data);
            request.onsuccess = function() { resolve('Data saved successfully'); };
            request.onerror = function(event) { reject('Save error: ' + event.target.errorCode); };
        });
    }

    async function extractAndSaveData() {
        try {
            const elements = document.querySelectorAll('a.flex.items-center');
            console.log('Elements found:', elements.length);
            const today = new Date().toISOString().split('T')[0];
            const db = await initDB();

            let count = 0;
            for (const el of elements) {
                const url = el.href;
                const innerTextElement = el.querySelector('div.relative.grow.overflow-hidden.whitespace-nowrap');
                const innerText = innerTextElement ? innerTextElement.innerText.trim() : '';

                if (!url || !innerText) {
                    continue;
                }

                const data = { url, innerText, firstSeen: today };

                const exists = await checkURLExists(db, url);
                if (!exists) {
                    await saveDataToDB(db, data);
                    console.log('New item found and saved:', data);
                    count++;
                }
            }

            console.log('Data extraction and saving completed. ', count, 'NEW items found and saved');
        } catch (error) {
            console.error(error);
        }
    }

    window.onload = function() {
        console.log('Page fully loaded, running extraction script.');
        setTimeout(() => {
            extractAndSaveData();
        }, 5000); // Delay of 5 seconds to ensure dynamic content is loaded
    };

    // Uncomment the below section for grabbing history (manually scrolling down first)
    /*
    function addButton() {
        const button = document.createElement('button');
        button.innerText = 'Extract Data';
        button.style.position = 'fixed';
        button.style.top = '10px';
        button.style.right = '10px';
        button.style.zIndex = 1000;
        button.style.padding = '10px';
        button.style.backgroundColor = '#4CAF50';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.onclick = extractAndSaveData;
        document.body.appendChild(button);
    }

    window.onload = function() {
        console.log('Page fully loaded, adding trigger button.');
        addButton();
    };
    */

})();
JS Bookmarklet
javascript:(function() {
// Function to initialize the IndexedDB
function initDB() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open('LinkedInDataDB', 1);

        request.onupgradeneeded = function(event) {
            const db = event.target.result;
            if (!db.objectStoreNames.contains('links')) {
                db.createObjectStore('links', { keyPath: 'id', autoIncrement: true });
            }
        };

        request.onsuccess = function(event) {
            resolve(event.target.result);
        };

        request.onerror = function(event) {
            reject('Database error: ' + event.target.errorCode);
        };
    });
}

// Function to save data to the IndexedDB
function saveDataToDB(db, data) {
    return new Promise((resolve, reject) => {
        const transaction = db.transaction(['links'], 'readwrite');
        const objectStore = transaction.objectStore('links');
        const request = objectStore.add(data);

        request.onsuccess = function() {
            resolve('Data saved successfully');
        };

        request.onerror = function(event) {
            reject('Save error: ' + event.target.errorCode);
        };
    });
}

// Function to extract data and save it to IndexedDB
async function extractAndSaveData() {
    try {
        const elements = document.querySelectorAll('a.flex.items-center.gap-2.p-2');
        const results = Array.from(elements).map(el => {
            const url = el.href;
            const innerTextElement = el.querySelector('div.relative.grow.overflow-hidden.whitespace-nowrap');
            const innerText = innerTextElement ? innerTextElement.innerText.trim() : '';
            return { url, innerText };
        });

        const db = await initDB();
        for (const result of results) {
            await saveDataToDB(db, result);
        }

        console.log('All data saved successfully', results);
    } catch (error) {
        console.error(error);
    }
}

extractAndSaveData();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment