Skip to content

Instantly share code, notes, and snippets.

@equinoxmatt
Last active July 6, 2024 18:57
Show Gist options
  • Save equinoxmatt/46993dd65b9a7f89f47af457f77f40c7 to your computer and use it in GitHub Desktop.
Save equinoxmatt/46993dd65b9a7f89f47af457f77f40c7 to your computer and use it in GitHub Desktop.
TamperMonkey script for removing multiple chats in ChatGPT
// ==UserScript==
// @name ChatGPT
// @version 0.1
// @description Allow deleting multiple chats at a time.
// @match https://chat.openai.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=openai.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Style definitions for checkboxes and the delete button
const css = `
li.relative[data-projection-id] {
position: relative;
padding-left: 40px; /* Make room for checkbox */
}
li.relative[data-projection-id] .custom-checkbox {
position: absolute;
left: 5px; /* Position from the left edge of the LI */
top: 50%;
transform: translateY(-50%);
z-index: 16; /* Ensure it's on top of the LI but below other potential overlays */
display: none; /* Hide the checkbox initially */
}
li.relative[data-projection-id]:hover .custom-checkbox,
li.relative[data-projection-id] .custom-checkbox:checked {
display: block; /* Show the checkbox on hover or when checked */
}
`;
// Function to add the CSS to the document head
function addStyles() {
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
document.head.appendChild(style);
}
// Function to add checkboxes to each list item
function addCheckboxes() {
document.querySelectorAll('li.relative[data-projection-id]').forEach(item => {
if (!item.querySelector('.custom-checkbox')) {
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.classList.add('custom-checkbox');
item.insertBefore(checkbox, item.firstChild);
}
});
}
// Function to add a delete button to the page
function addDeleteButton() {
const targetContainer = document.querySelector('div.gap-2:nth-child(3)');
if (targetContainer && !document.getElementById('delete-button')) {
const deleteButton = document.createElement('button');
deleteButton.id = 'delete-button';
deleteButton.textContent = 'Delete';
deleteButton.style.display = 'block';
deleteButton.addEventListener('click', handleDeleteAction);
targetContainer.appendChild(deleteButton);
}
}
// Function to update the visibility of the delete button
function updateDeleteButtonVisibility() {
const anyChecked = document.querySelectorAll('li.relative[data-projection-id] .custom-checkbox:checked').length > 0;
const deleteButton = document.getElementById('delete-button');
if (deleteButton) {
deleteButton.style.display = anyChecked ? 'block' : 'none';
}
}
// Function to fetch the access token
function fetchAccessToken() {
return fetch('https://chat.openai.com/api/auth/session', {
credentials: 'include'
})
.then(response => response.json())
.then(data => data.accessToken)
.catch(error => {
console.error('Error fetching access token:', error);
return null;
});
}
// Initialization function
function init() {
addStyles();
addCheckboxes();
setTimeout(() => {
addDeleteButton();
updateDeleteButtonVisibility();
}, 0);
}
// Delete action handler
function handleDeleteAction() {
fetchAccessToken().then(accessToken => {
if (!accessToken) {
console.error('No access token retrieved.');
return;
}
document.querySelectorAll('li.relative[data-projection-id] .custom-checkbox:checked').forEach(checkbox => {
const parentLi = checkbox.closest('li.relative[data-projection-id]');
const href = parentLi.querySelector('a').getAttribute('href');
const id = href.substring(href.lastIndexOf('/') + 1);
fetch('https://chat.openai.com/backend-api/conversation/' + id, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({ is_visible: false })
})
.then(response => response.json())
.then(data => {
console.log('Delete successful', data);
parentLi.remove();
})
.catch(error => {
console.error('Error:', error);
});
});
});
}
// Event listener for checkbox state changes
document.addEventListener('change', e => {
if (e.target.classList.contains('custom-checkbox')) {
updateDeleteButtonVisibility();
}
});
// Mutation observer for dynamically added content
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.addedNodes.length) {
addDeleteButton();
addCheckboxes();
updateDeleteButtonVisibility();
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
// Start the script based on the document's loading state
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment