Skip to content

Instantly share code, notes, and snippets.

@hclivess
Created March 18, 2025 12:36
Show Gist options
  • Save hclivess/81e9c15e02fd94d931ba63c1987459bf to your computer and use it in GitHub Desktop.
Save hclivess/81e9c15e02fd94d931ba63c1987459bf to your computer and use it in GitHub Desktop.
Unsubscribe all GMAIL emails for free, open source
// Gmail Mass Unsubscribe - Uses pagination to process ALL emails
// Run this in your browser console while in Gmail
(async function() {
console.log('πŸš€ Starting Gmail unsubscribe for ALL emails using pagination...');
// Constants
const EMAIL_OPEN_DELAY = 1000;
const MAX_PAGES = 200; // Safety limit on pages to process
// Global trackers
window.unsubUrls = [];
let totalProcessed = 0;
let totalFound = 0;
// First search for "unsubscribe" if not already searched
if (!window.location.hash.includes('#search/unsubscribe')) {
console.log('πŸ“§ Searching for emails with "unsubscribe"...');
window.location.hash = '#search/unsubscribe';
await new Promise(resolve => setTimeout(resolve, 3000));
}
// Function to get the current page number
function getCurrentPage() {
// Check pagination controls
const paginationText = document.querySelector('.Dj');
if (!paginationText) return 1;
const text = paginationText.textContent;
const match = text.match(/(\d+)β€’(\d+)/);
if (!match) return 1;
return Math.ceil(parseInt(match[1]) / 50); // Assuming 50 per page
}
// Function to get total emails shown in pagination
function getTotalEmailCount() {
const paginationText = document.querySelector('.Dj');
if (!paginationText) return 0;
const text = paginationText.textContent;
const match = text.match(/of\s+(\d+)/);
if (!match) return 0;
return parseInt(match[1]);
}
// Function to go to next page
async function goToNextPage() {
const nextButton = document.querySelector('[act="20"]'); // Gmail's next page button
if (!nextButton) return false;
console.log('πŸ“„ Going to next page...');
nextButton.click();
await new Promise(resolve => setTimeout(resolve, 2000));
return true;
}
// Function to process emails on current page
async function processCurrentPage() {
console.log(`πŸ“„ Processing page ${getCurrentPage()}...`);
// Get all emails on this page
const emails = document.querySelectorAll('tr.zA');
console.log(`πŸ” Found ${emails.length} emails on this page`);
let pageProcessed = 0;
let pageFound = 0;
// Process each email on this page
for (let i = 0; i < emails.length; i++) {
const email = emails[i];
// Check if email content suggests it's a subscription
const emailText = email.textContent.toLowerCase();
if (emailText.includes('unsubscribe') ||
emailText.includes('opt out') ||
emailText.includes('opt-out') ||
emailText.includes('subscription') ||
emailText.includes('newsletter')) {
// Highlight for visibility
const originalBackground = email.style.backgroundColor;
email.style.backgroundColor = '#e6f7ff';
try {
// Get email details before opening
const emailSender = email.querySelector('.yW')?.textContent || 'Unknown';
const emailSubjectElem = email.querySelector('.y6');
const emailSubject = emailSubjectElem?.textContent || 'No Subject';
// Click to open the email
if (emailSubjectElem) {
emailSubjectElem.click();
await new Promise(resolve => setTimeout(resolve, EMAIL_OPEN_DELAY));
// Check for unsubscribe options
const emailBody = document.querySelector('.a3s');
if (emailBody) {
// First try Gmail's built-in unsubscribe button
const gmailUnsubButton = document.querySelector('[aria-label="Unsubscribe"]');
if (gmailUnsubButton) {
console.log(`βœ… Found Gmail unsubscribe for: ${emailSubject}`);
gmailUnsubButton.click();
await new Promise(resolve => setTimeout(resolve, 1000));
// Confirm unsubscribe if dialog appears
const confirmButtons = Array.from(document.querySelectorAll('button')).filter(b =>
b.textContent.toLowerCase().includes('unsubscribe') ||
b.textContent.toLowerCase().includes('confirm')
);
if (confirmButtons.length > 0) {
confirmButtons[0].click();
console.log(`βœ“ Confirmed unsubscribe for: ${emailSubject}`);
}
pageFound++;
totalFound++;
} else {
// Look for unsubscribe links in email body
const allLinks = emailBody.querySelectorAll('a');
const unsubLinks = Array.from(allLinks).filter(link => {
const linkText = (link.textContent || '').toLowerCase();
const linkHref = (link.href || '').toLowerCase();
return linkText.includes('unsubscribe') ||
linkText.includes('opt out') ||
linkText.includes('opt-out') ||
linkHref.includes('unsubscribe') ||
linkHref.includes('opt-out');
});
if (unsubLinks.length > 0) {
console.log(`βœ… Found unsubscribe link for: ${emailSubject}`);
// Get the first unsubscribe link
const unsubLink = unsubLinks[0];
const unsubUrl = unsubLink.href;
// Store the URL
window.unsubUrls.push({
sender: emailSender,
subject: emailSubject,
url: unsubUrl
});
// Open in a new tab
window.open(unsubUrl, '_blank');
pageFound++;
totalFound++;
}
}
}
// Go back to inbox
const backButton = document.querySelector('[aria-label="Back to Inbox"]');
if (backButton) {
backButton.click();
await new Promise(resolve => setTimeout(resolve, 800));
}
}
} catch (error) {
console.error(`Error processing email: ${error.message}`);
}
// Reset background color
email.style.backgroundColor = originalBackground;
pageProcessed++;
totalProcessed++;
}
}
console.log(`πŸ“Š Page summary: Processed ${pageProcessed} emails, found ${pageFound} unsubscribe links`);
return { pageProcessed, pageFound };
}
// Process all pages with pagination
async function processAllPages() {
console.log('πŸ”„ Starting pagination-based processing of ALL emails...');
// Get total emails count
const totalEmails = getTotalEmailCount();
console.log(`πŸ“Š Total emails found in search: ${totalEmails}`);
// Process first page
await processCurrentPage();
// Process subsequent pages
let currentPage = 1;
let hasMorePages = true;
while (hasMorePages && currentPage < MAX_PAGES) {
hasMorePages = await goToNextPage();
if (hasMorePages) {
currentPage++;
console.log(`πŸ“„ Now on page ${currentPage} of search results`);
await processCurrentPage();
}
}
return { totalProcessed, totalFound, totalEmails };
}
// Start processing
console.log('πŸ” Starting to process ALL pages of emails for unsubscribe...');
const result = await processAllPages();
// Final report
console.log(`✨ PAGINATION PROCESS COMPLETE! ✨`);
console.log(`πŸ“Š Total emails in search: ${result.totalEmails}`);
console.log(`πŸ“Š Total subscription emails processed: ${result.totalProcessed}`);
console.log(`πŸ”— Total unsubscribe links found: ${result.totalFound}`);
// Show all unsubscribe URLs
console.log('πŸ“‹ All unsubscribe links found:');
window.unsubUrls.forEach((item, index) => {
console.log(`${index+1}. ${item.sender}: ${item.subject}`);
console.log(` πŸ”— ${item.url}`);
});
console.log(`⚠️ Note: Unsubscribe links were opened in new tabs. Please check those tabs to complete the process.`);
console.log(`πŸ’‘ All links are stored in "window.unsubUrls" if you need them again.`);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment