Greasy Fork

Greasy Fork is available in English.

Upload To Gitlab Button for Claude.ai Chat

Adds an "Upload To Gitlab" button before the chat controls on Claude.ai chat pages

目前为 2024-04-10 提交的版本。查看 最新版本

// ==UserScript==
// @name         Upload To Gitlab Button for Claude.ai Chat
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Adds an "Upload To Gitlab" button before the chat controls on Claude.ai chat pages
// @match        https://claude.ai/chat/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Function to create the "Upload To Gitlab" button
    function createUploadToGitlabButton() {
        const uploadButton = document.createElement('button');
        uploadButton.textContent = 'Upload To Gitlab';
        uploadButton.id = 'upload-to-gitlab-btn';
        uploadButton.style.backgroundColor = 'blue';
        uploadButton.style.color = 'white';
        uploadButton.style.padding = '5px 10px';
        uploadButton.style.borderRadius = '5px';
        uploadButton.style.marginRight = '10px';
        uploadButton.addEventListener('click', openModal);
        return uploadButton;
    }

    // Function to open the modal
    function openModal() {
        const modal = document.createElement('div');
        modal.id = 'gitlab-modal';
        modal.style.position = 'fixed';
        modal.style.top = '50%';
        modal.style.left = '50%';
        modal.style.transform = 'translate(-50%, -50%)';
        modal.style.backgroundColor = 'hsl(var(--bg-200)/var(--tw-bg-opacity))';
        modal.style.padding = '20px';
        modal.style.borderRadius = '5px';
        modal.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.3)';
        modal.style.zIndex = '9999';
        modal.innerHTML = `
            <h2>Upload To Gitlab</h2>
            <form id="gitlab-form">
                <label for="gitlab-domain">Gitlab Domain:</label>
                <input type="text" id="gitlab-domain" name="gitlab-domain" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="gitlab-token">Gitlab Token:</label>
                <input type="text" id="gitlab-token" name="gitlab-token" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="gitlab-project-id">Gitlab Project ID:</label>
                <input type="text" id="gitlab-project-id" name="gitlab-project-id" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="project-branch">Project Branch:</label>
                <input type="text" id="project-branch" name="project-branch" required style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <label for="file-path">Path:</label>
                <input type="text" id="file-path" name="file-path" style="background-color: hsl(var(--bg-200)/var(--tw-bg-opacity));"><br>
                <div style="text-align: right;">
                    <button type="button" id="close-modal-btn" style="margin-right: 10px;">X</button>
                    <button type="submit" style="background-color: green; color: white;">Upload</button>
                </div>
            </form>
        `;
        document.body.appendChild(modal);

        // Populate form fields with saved data
        const gitlabDomain = localStorage.getItem('gitlab-domain');
        const gitlabToken = localStorage.getItem('gitlab-token');
        const gitlabProjectId = localStorage.getItem('gitlab-project-id');
        const projectBranch = localStorage.getItem('project-branch');
        const filePath = localStorage.getItem('file-path');
        if (gitlabDomain) {
            document.getElementById('gitlab-domain').value = gitlabDomain;
        }
        if (gitlabToken) {
            document.getElementById('gitlab-token').value = gitlabToken;
        }
        if (gitlabProjectId) {
            document.getElementById('gitlab-project-id').value = gitlabProjectId;
        }
        if (projectBranch) {
            document.getElementById('project-branch').value = projectBranch;
        }
        if (filePath) {
            document.getElementById('file-path').value = filePath;
        }

        // Handle form submission
        document.getElementById('gitlab-form').addEventListener('submit', function(e) {
            e.preventDefault();
            const gitlabDomain = document.getElementById('gitlab-domain').value.replace(/^https?:\/\//, '').replace(/\/$/, '');
            const gitlabToken = document.getElementById('gitlab-token').value;
            const gitlabProjectId = document.getElementById('gitlab-project-id').value;
            const projectBranch = document.getElementById('project-branch').value;
            const filePath = document.getElementById('file-path').value.trim();

            // Save form data in localStorage
            localStorage.setItem('gitlab-domain', gitlabDomain);
            localStorage.setItem('gitlab-token', gitlabToken);
            localStorage.setItem('gitlab-project-id', gitlabProjectId);
            localStorage.setItem('project-branch', projectBranch);
            localStorage.setItem('file-path', filePath);

            // Close the modal
            document.body.removeChild(modal);

            // Fetch data from the API and upload to Gitlab
            uploadToGitlab(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, filePath);
        });

        // Handle modal close button click
        document.getElementById('close-modal-btn').addEventListener('click', function() {
            document.body.removeChild(modal);
        });
    }

    // Function to check if the file exists on Gitlab
    function checkFileExists(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, filePath) {
        const checkFileUrl = `https://${gitlabDomain}/api/v4/projects/${gitlabProjectId}/repository/files/${encodeURIComponent(filePath)}?ref=${projectBranch}`;
        return fetch(checkFileUrl, {
            method: 'GET',
            headers: {
                'PRIVATE-TOKEN': gitlabToken
            }
        }).then(response => {
            if (response.status === 200) {
                return true;
            } else if (response.status === 404) {
                return false;
            } else {
                throw new Error(`Unexpected response status: ${response.status}`);
            }
        });
    }

    // Function to upload data to Gitlab
    function uploadToGitlab(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, userFilePath) {
        const cacheName = 'apis';
        caches.open(cacheName).then(function(cache) {
            cache.keys().then(function(requests) {
                let organizationId = null;
                requests.forEach(function(request) {
                    if (request.url.includes('https://claude.ai/api/organizations/')) {
                        const parts = request.url.split('/');
                        const index = parts.indexOf('organizations');
                        if (index !== -1 && parts.length > index + 1) {
                            organizationId = parts[index + 1];
                        }
                    }
                });
                console.log('Organization ID (from cache):', organizationId);

                // Get the current chat ID from the browser URL
                const currentUrl = window.location.href;
                const chatId = currentUrl.split('/').pop();
                console.log('Current Chat ID (from URL):', chatId);

                // Fetch JSON data from the API URL
                const apiUrl = `https://claude.ai/api/organizations/${organizationId}/chat_conversations/${chatId}`;
                fetch(apiUrl)
                    .then(response => response.json())
                    .then(data => {
                        console.log('API Response:', data);

                        const filePath = userFilePath ? `${userFilePath.replace(/\/$/, '')}/${chatId}.json` : `${chatId}.json`;
                        checkFileExists(gitlabDomain, gitlabToken, gitlabProjectId, projectBranch, filePath)
                            .then(fileExists => {
                                const action = fileExists ? 'update' : 'create';

                                // Prepare the payload for Gitlab API request
                                const payload = {
                                    branch: projectBranch,
                                    commit_message: `${action === 'create' ? 'Add' : 'Update'} chat: ${data.name}`,
                                    actions: [
                                        {
                                            action: action,
                                            file_path: filePath,
                                            content: JSON.stringify(data, null, 2)
                                        }
                                    ]
                                };

                                // Make the Gitlab API request to create or update the file
                                const gitlabUrl = `https://${gitlabDomain}/api/v4/projects/${gitlabProjectId}/repository/commits`;
                                fetch(gitlabUrl, {
                                    method: 'POST',
                                    headers: {
                                        'PRIVATE-TOKEN': gitlabToken,
                                        'Content-Type': 'application/json'
                                    },
                                    body: JSON.stringify(payload)
                                })
                                    .then(response => {
                                        if (response.status === 401) {
                                            alert('Unauthorized: Invalid Gitlab token');
                                        } else if (response.status !== 201) {
                                            return response.json().then(data => {
                                                throw new Error(JSON.stringify(data));
                                            });
                                        } else {
                                            return response.json();
                                        }
                                    })
                                    .then(data => {
                                        console.log('Gitlab API Response:', data);
                                        window.open(data.web_url, '_blank');
                                    })
                                    .catch(error => {
                                        console.error('Error uploading to Gitlab:', error);
                                        alert(`Failed to upload chat to Gitlab. Error: ${error.message}`);
                                    });
                            })
                            .catch(error => {
                                console.error('Error checking file existence:', error);
                                alert('Failed to check file existence on Gitlab.');
                            });
                    })
                    .catch(error => {
                        console.error('Error fetching API data:', error);
                        alert('Failed to fetch chat data from the API.');
                    });
            });
        });
    }

    // Function to insert the "Upload To Gitlab" button before the chat controls
    function insertUploadToGitlabButton() {
        const chatControls = document.querySelector('[data-testid="chat-controls"]');
        const existingButton = document.getElementById('upload-to-gitlab-btn');
        if (chatControls && !existingButton) {
            const uploadButton = createUploadToGitlabButton();
            chatControls.parentNode.insertBefore(uploadButton, chatControls);
        }
    }

    // Observe changes in the DOM and insert the "Upload To Gitlab" button when the chat controls appear
    const observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                insertUploadToGitlabButton();
            }
        });
    });

    // Configure the observer to watch for changes in the entire document
    const config = { childList: true, subtree: true };
    observer.observe(document.body, config);

    // Insert the "Upload To Gitlab" button initially if the chat controls are already present
    insertUploadToGitlabButton();
})();