email-validation / index.html
sqibhe's picture
Add 2 files
2bae974 verified
Raw History Blame Contribute Delete
67.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Advanced Email Validation Bot</title> <script src="https://cdn.tailwindcss.com"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> .dropzone { border: 2px dashed #4b5563; border-radius: 0.5rem; transition: all 0.3s ease; } .dropzone.active { border-color: #3b82f6; background-color: rgba(59, 130, 246, 0.05); } .progress-bar { transition: width 0.3s ease; } .tooltip { position: relative; display: inline-block; } .tooltip .tooltip-text { visibility: hidden; width: 200px; background-color: #1f2937; color: #fff; text-align: center; border-radius: 6px; padding: 5px; position: absolute; z-index: 1; bottom: 125%; left: 50%; margin-left: -100px; opacity: 0; transition: opacity 0.3s; } .tooltip:hover .tooltip-text { visibility: visible; opacity: 1; } .status-badge { padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; } .status-valid { background-color: #d1fae5; color: #065f46; } .status-invalid { background-color: #fee2e2; color: #b91c1c; } .status-duplicate { background-color: #fef3c7; color: #92400e; } .status-typo { background-color: #dbeafe; color: #1e40af; } .status-unknown { background-color: #e5e7eb; color: #4b5563; } .status-undeliverable { background-color: #f5f5f4; color: #57534e; } .status-mx { background-color: #ede9fe; color: #5b21b6; } .fade-in { animation: fadeIn 0.5s ease-in; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .insight-card { transition: all 0.3s ease; } .insight-card:hover { transform: translateY(-2px); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); } </style> </head> <body class="bg-gray-50 min-h-screen"> <div class="container mx-auto px-4 py-8 max-w-6xl"> <div class="text-center mb-8"> <h1 class="text-3xl font-bold text-gray-800 mb-2">Advanced Email Validation Bot</h1> <p class="text-gray-600">Upload your Excel file to validate and clean email addresses</p> </div> <div class="bg-white rounded-lg shadow-md p-6 mb-8"> <div id="upload-section"> <div class="dropzone p-8 text-center cursor-pointer" id="dropzone"> <div class="flex flex-col items-center justify-center"> <i class="fas fa-file-excel text-4xl text-blue-500 mb-4"></i> <h3 class="text-lg font-medium text-gray-700 mb-2">Drag & Drop your Excel file here</h3> <p class="text-gray-500 mb-4">or</p> <label for="file-upload" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md cursor-pointer transition duration-300"> <i class="fas fa-upload mr-2"></i> Browse Files </label> <input id="file-upload" type="file" accept=".xlsx, .xls, .csv" class="hidden"> </div> </div> <div class="mt-4 text-sm text-gray-500"> <p>Supported formats: .xlsx, .xls, .csv</p> <p>File should contain at least one column with email addresses</p> </div> </div> <div id="processing-section" class="hidden"> <div class="flex items-center mb-4"> <div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center mr-3"> <i class="fas fa-cog text-blue-500 animate-spin"></i> </div> <h3 class="text-lg font-medium text-gray-700">Processing your file...</h3> </div> <div class="mb-4"> <div class="flex justify-between text-sm text-gray-600 mb-1"> <span>Progress</span> <span id="progress-percent">0%</span> </div> <div class="w-full bg-gray-200 rounded-full h-2.5"> <div id="progress-bar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div> </div> </div> <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4"> <div class="bg-gray-50 p-4 rounded-lg"> <div class="text-sm text-gray-500 mb-1">Emails Processed</div> <div class="text-2xl font-bold text-gray-800" id="processed-count">0</div> </div> <div class="bg-gray-50 p-4 rounded-lg"> <div class="text-sm text-gray-500 mb-1">Valid Emails</div> <div class="text-2xl font-bold text-green-600" id="valid-count">0</div> </div> <div class="bg-gray-50 p-4 rounded-lg"> <div class="text-sm text-gray-500 mb-1">Issues Found</div> <div class="text-2xl font-bold text-red-600" id="issues-count">0</div> </div> </div> </div> <div id="results-section" class="hidden"> <div class="flex items-center mb-4"> <div class="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center mr-3"> <i class="fas fa-check text-green-500"></i> </div> <h3 class="text-lg font-medium text-gray-700">Validation Complete</h3> </div> <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="text-sm text-gray-500 mb-1">Total Emails</div> <div class="text-2xl font-bold text-gray-800" id="total-emails">0</div> </div> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="text-sm text-gray-500 mb-1">Valid Emails</div> <div class="text-2xl font-bold text-green-600" id="final-valid">0</div> </div> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="text-sm text-gray-500 mb-1">Invalid Emails</div> <div class="text-2xl font-bold text-red-600" id="final-invalid">0</div> </div> </div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6"> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="flex items-center justify-between mb-2"> <div class="text-sm text-gray-500">Duplicates</div> <div class="text-lg font-bold text-amber-600" id="duplicate-count">0</div> </div> <div class="text-xs text-gray-400">Emails appearing multiple times</div> </div> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="flex items-center justify-between mb-2"> <div class="text-sm text-gray-500">Typos Found</div> <div class="text-lg font-bold text-blue-600" id="typo-count">0</div> </div> <div class="text-xs text-gray-400">Potential email address typos</div> </div> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="flex items-center justify-between mb-2"> <div class="text-sm text-gray-500">Syntax Errors</div> <div class="text-lg font-bold text-red-600" id="syntax-count">0</div> </div> <div class="text-xs text-gray-400">Invalid email formats</div> </div> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="flex items-center justify-between mb-2"> <div class="text-sm text-gray-500">MX Issues</div> <div class="text-lg font-bold text-purple-600" id="mx-count">0</div> </div> <div class="text-xs text-gray-400">Domains without mail servers</div> </div> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="flex items-center justify-between mb-2"> <div class="text-sm text-gray-500">Undeliverable</div> <div class="text-lg font-bold text-gray-600" id="undeliverable-count">0</div> </div> <div class="text-xs text-gray-400">Emails that may bounce</div> </div> <div class="bg-white border border-gray-200 p-4 rounded-lg shadow-sm"> <div class="flex items-center justify-between mb-2"> <div class="text-sm text-gray-500">Unknown Status</div> <div class="text-lg font-bold text-gray-500" id="unknown-count">0</div> </div> <div class="text-xs text-gray-400">Could not verify deliverability</div> </div> </div> <!-- MX Response Insights Section --> <div class="mb-6"> <h4 class="font-medium text-gray-700 mb-4 flex items-center"> <i class="fas fa-server mr-2 text-blue-500"></i> MX Record Analysis </h4> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="bg-white border border-blue-100 rounded-lg p-4 insight-card"> <div class="flex items-center mb-2"> <div class="w-10 h-10 bg-blue-50 rounded-full flex items-center justify-center mr-3"> <i class="fas fa-check-circle text-blue-500"></i> </div> <h5 class="font-medium text-gray-800">Valid MX Records</h5> </div> <div class="text-3xl font-bold text-blue-600 mb-2" id="valid-mx-count">0</div> <div class="text-sm text-gray-600"> <span id="valid-mx-percent">0</span>% of emails have domains with properly configured mail servers </div> </div> <div class="bg-white border border-purple-100 rounded-lg p-4 insight-card"> <div class="flex items-center mb-2"> <div class="w-10 h-10 bg-purple-50 rounded-full flex items-center justify-center mr-3"> <i class="fas fa-exclamation-triangle text-purple-500"></i> </div> <h5 class="font-medium text-gray-800">MX Issues Detected</h5> </div> <div class="text-3xl font-bold text-purple-600 mb-2" id="invalid-mx-count">0</div> <div class="text-sm text-gray-600"> These domains cannot receive emails due to missing or incorrect MX records </div> <div class="mt-2"> <div class="text-xs font-medium text-gray-500 mb-1">Top Problematic Domains:</div> <div class="text-xs text-gray-600" id="top-mx-issues"></div> </div> </div> </div> </div> <!-- Deliverability Insights Section --> <div class="mb-6"> <h4 class="font-medium text-gray-700 mb-4 flex items-center"> <i class="fas fa-paper-plane mr-2 text-green-500"></i> Email Deliverability Insights </h4> <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="bg-white border border-green-100 rounded-lg p-4 insight-card"> <div class="flex items-center mb-2"> <div class="w-10 h-10 bg-green-50 rounded-full flex items-center justify-center mr-3"> <i class="fas fa-check-circle text-green-500"></i> </div> <h5 class="font-medium text-gray-800">High Deliverability</h5> </div> <div class="text-3xl font-bold text-green-600 mb-2" id="deliverable-count">0</div> <div class="text-sm text-gray-600"> <span id="deliverable-percent">0</span>% of emails are confirmed to be deliverable </div> </div> <div class="bg-white border border-red-100 rounded-lg p-4 insight-card"> <div class="flex items-center mb-2"> <div class="w-10 h-10 bg-red-50 rounded-full flex items-center justify-center mr-3"> <i class="fas fa-times-circle text-red-500"></i> </div> <h5 class="font-medium text-gray-800">Undeliverable</h5> </div> <div class="text-3xl font-bold text-red-600 mb-2" id="undeliverable-insight-count">0</div> <div class="text-sm text-gray-600"> These emails will likely bounce or be rejected by receiving servers </div> </div> <div class="bg-white border border-gray-200 rounded-lg p-4 insight-card"> <div class="flex items-center mb-2"> <div class="w-10 h-10 bg-gray-50 rounded-full flex items-center justify-center mr-3"> <i class="fas fa-question-circle text-gray-500"></i> </div> <h5 class="font-medium text-gray-800">Unknown Status</h5> </div> <div class="text-3xl font-bold text-gray-600 mb-2" id="unknown-deliverability-count">0</div> <div class="text-sm text-gray-600"> Could not verify deliverability due to server restrictions or timeouts </div> </div> </div> <div class="mt-4 bg-white border border-yellow-100 rounded-lg p-4 insight-card"> <div class="flex items-center mb-2"> <div class="w-10 h-10 bg-yellow-50 rounded-full flex items-center justify-center mr-3"> <i class="fas fa-chart-line text-yellow-500"></i> </div> <h5 class="font-medium text-gray-800">Overall Deliverability Score</h5> </div> <div class="flex items-center"> <div class="w-full bg-gray-200 rounded-full h-4 mr-4"> <div id="deliverability-score-bar" class="h-4 rounded-full" style="width: 0%; background: linear-gradient(90deg, #10b981 0%, #f59e0b 50%, #ef4444 100%);"></div> </div> <div class="text-2xl font-bold" id="deliverability-score">0</div> <span class="ml-1 text-gray-500">/100</span> </div> <div class="mt-2 text-sm text-gray-600"> <span id="deliverability-rating" class="font-medium">Poor</span> - <span id="deliverability-description">This list has significant deliverability issues that need attention</span> </div> </div> </div> <div class="mb-6"> <div class="flex justify-between items-center mb-2"> <h4 class="font-medium text-gray-700">Email Details</h4> <div class="text-sm text-gray-500"> <span id="showing-count">0</span> of <span id="total-count">0</span> emails shown </div> </div> <div class="overflow-x-auto"> <table class="min-w-full bg-white border border-gray-200 rounded-lg overflow-hidden"> <thead class="bg-gray-50"> <tr> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Details</th> <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">MX Response</th> </tr> </thead> <tbody id="email-table-body" class="divide-y divide-gray-200"> <!-- Email rows will be inserted here --> </tbody> </table> </div> </div> <div class="flex flex-col sm:flex-row justify-between gap-4"> <button id="download-report" class="flex-1 bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-md flex items-center justify-center transition duration-300"> <i class="fas fa-file-excel mr-2"></i> Download Cleaned Excel File </button> <button id="download-summary" class="flex-1 bg-gray-100 hover:bg-gray-200 text-gray-700 px-6 py-3 rounded-md flex items-center justify-center transition duration-300"> <i class="fas fa-file-alt mr-2"></i> Download Full Report (PDF) </button> <button id="start-over" class="flex-1 bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 px-6 py-3 rounded-md flex items-center justify-center transition duration-300"> <i class="fas fa-redo mr-2"></i> Start Over </button> </div> </div> </div> <div class="bg-white rounded-lg shadow-md p-6 mb-8 hidden" id="error-section"> <div class="flex items-center mb-4"> <div class="w-8 h-8 rounded-full bg-red-100 flex items-center justify-center mr-3"> <i class="fas fa-exclamation-triangle text-red-500"></i> </div> <h3 class="text-lg font-medium text-gray-700">Error Processing File</h3> </div> <div class="text-red-600 mb-4" id="error-message"></div> <button id="try-again" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md transition duration-300"> <i class="fas fa-redo mr-2"></i> Try Again </button> </div> <div class="bg-white rounded-lg shadow-md p-6"> <h3 class="text-lg font-medium text-gray-700 mb-4">How It Works</h3> <div class="grid grid-cols-1 md:grid-cols-4 gap-4"> <div class="bg-gray-50 p-4 rounded-lg"> <div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mb-3"> <i class="fas fa-file-upload text-blue-500"></i> </div> <h4 class="font-medium text-gray-800 mb-1">1. Upload File</h4> <p class="text-sm text-gray-600">Upload your Excel file containing email addresses</p> </div> <div class="bg-gray-50 p-4 rounded-lg"> <div class="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center mb-3"> <i class="fas fa-check-circle text-purple-500"></i> </div> <h4 class="font-medium text-gray-800 mb-1">2. Validation</h4> <p class="text-sm text-gray-600">We check each email for syntax, typos, duplicates, and deliverability</p> </div> <div class="bg-gray-50 p-4 rounded-lg"> <div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center mb-3"> <i class="fas fa-chart-pie text-green-500"></i> </div> <h4 class="font-medium text-gray-800 mb-1">3. Analysis</h4> <p class="text-sm text-gray-600">Get detailed statistics about your email list quality</p> </div> <div class="bg-gray-50 p-4 rounded-lg"> <div class="w-10 h-10 bg-amber-100 rounded-full flex items-center justify-center mb-3"> <i class="fas fa-download text-amber-500"></i> </div> <h4 class="font-medium text-gray-800 mb-1">4. Download</h4> <p class="text-sm text-gray-600">Download cleaned file with validation results and suggestions</p> </div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // DOM elements const dropzone = document.getElementById('dropzone'); const fileUpload = document.getElementById('file-upload'); const uploadSection = document.getElementById('upload-section'); const processingSection = document.getElementById('processing-section'); const resultsSection = document.getElementById('results-section'); const errorSection = document.getElementById('error-section'); const emailTableBody = document.getElementById('email-table-body'); // Progress elements const progressBar = document.getElementById('progress-bar'); const progressPercent = document.getElementById('progress-percent'); const processedCount = document.getElementById('processed-count'); const validCount = document.getElementById('valid-count'); const issuesCount = document.getElementById('issues-count'); // Result elements const totalEmails = document.getElementById('total-emails'); const finalValid = document.getElementById('final-valid'); const finalInvalid = document.getElementById('final-invalid'); const duplicateCount = document.getElementById('duplicate-count'); const typoCount = document.getElementById('typo-count'); const syntaxCount = document.getElementById('syntax-count'); const mxCount = document.getElementById('mx-count'); const undeliverableCount = document.getElementById('undeliverable-count'); const unknownCount = document.getElementById('unknown-count'); const showingCount = document.getElementById('showing-count'); const totalCount = document.getElementById('total-count'); // MX Insights elements const validMxCount = document.getElementById('valid-mx-count'); const validMxPercent = document.getElementById('valid-mx-percent'); const invalidMxCount = document.getElementById('invalid-mx-count'); const topMxIssues = document.getElementById('top-mx-issues'); // Deliverability Insights elements const deliverableCount = document.getElementById('deliverable-count'); const deliverablePercent = document.getElementById('deliverable-percent'); const undeliverableInsightCount = document.getElementById('undeliverable-insight-count'); const unknownDeliverabilityCount = document.getElementById('unknown-deliverability-count'); const deliverabilityScoreBar = document.getElementById('deliverability-score-bar'); const deliverabilityScore = document.getElementById('deliverability-score'); const deliverabilityRating = document.getElementById('deliverability-rating'); const deliverabilityDescription = document.getElementById('deliverability-description'); // Buttons const downloadReport = document.getElementById('download-report'); const downloadSummary = document.getElementById('download-summary'); const startOver = document.getElementById('start-over'); const tryAgain = document.getElementById('try-again'); // Data storage let emailData = []; let processedEmails = []; let workbook = null; let domainStats = {}; // Common typos mapping const commonTypos = { 'gmai.com': 'gmail.com', 'gmail.con': 'gmail.com', 'gmail.co': 'gmail.com', 'gmail.cm': 'gmail.com', 'gmial.com': 'gmail.com', 'gmal.com': 'gmail.com', 'gamil.com': 'gmail.com', 'gnail.com': 'gmail.com', 'gmsil.com': 'gmail.com', 'gmaill.com': 'gmail.com', 'gmail..com': 'gmail.com', 'gmail.comm': 'gmail.com', 'gmail.om': 'gmail.com', 'gmil.com': 'gmail.com', 'gmaul.com': 'gmail.com', 'gmaio.com': 'gmail.com', 'gmaiil.com': 'gmail.com', 'gmeil.com': 'gmail.com', 'gmil.com': 'gmail.com', 'gmaile.com': 'gmail.com', 'gmaim.com': 'gmail.com', 'hotnail.com': 'hotmail.com', 'homail.com': 'hotmail.com', 'hotmal.com': 'hotmail.com', 'hotmaill.com': 'hotmail.com', 'hotmail..com': 'hotmail.com', 'yahoo.co': 'yahoo.com', 'yaho.com': 'yahoo.com', 'yahho.com': 'yahoo.com', 'yahoocom': 'yahoo.com', 'yahooo.com': 'yahoo.com', 'yahoo.comm': 'yahoo.com', 'outlok.com': 'outlook.com', 'outllok.com': 'outlook.com', 'outlook..com': 'outlook.com' }; // MX response messages const mxResponses = { valid: "Valid MX records found. Domain can receive emails.", invalid: "No MX records found. Domain cannot receive emails.", timeout: "MX lookup timed out. Could not verify mail servers.", restricted: "MX lookup restricted by DNS settings.", temporary: "Temporary MX lookup failure. Try again later." }; // Deliverability messages const deliverabilityMessages = { deliverable: "Server confirmed email exists and can receive messages.", undeliverable: "Server confirmed email does not exist or cannot receive messages.", unknown: "Could not verify deliverability due to server restrictions.", risky: "Email appears valid but may have deliverability issues.", catchall: "Domain accepts all emails (catch-all), making verification less reliable." }; // Event listeners fileUpload.addEventListener('change', handleFileSelect); dropzone.addEventListener('dragover', handleDragOver); dropzone.addEventListener('dragleave', handleDragLeave); dropzone.addEventListener('drop', handleDrop); downloadReport.addEventListener('click', downloadCleanedFile); downloadSummary.addEventListener('click', downloadSummaryReport); startOver.addEventListener('click', resetForm); tryAgain.addEventListener('click', resetForm); // Functions function handleFileSelect(e) { const files = e.target.files; if (files.length) { processFile(files[0]); } } function handleDragOver(e) { e.preventDefault(); e.stopPropagation(); dropzone.classList.add('active'); } function handleDragLeave(e) { e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('active'); } function handleDrop(e) { e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('active'); const files = e.dataTransfer.files; if (files.length) { processFile(files[0]); } } function processFile(file) { const reader = new FileReader(); reader.onload = function(e) { try { const data = new Uint8Array(e.target.result); workbook = XLSX.read(data, { type: 'array' }); // Extract all emails from the workbook emailData = extractEmailsFromWorkbook(workbook); if (emailData.length === 0) { showError("No email addresses found in the file. Please upload a file with at least one column containing email addresses."); return; } // Initialize domain stats initDomainStats(emailData); // Show processing section uploadSection.classList.add('hidden'); processingSection.classList.remove('hidden'); resultsSection.classList.add('hidden'); errorSection.classList.add('hidden'); // Start processing emails processEmails(emailData); } catch (error) { showError("Error reading the file. Please make sure it's a valid Excel file."); console.error(error); } }; reader.onerror = function() { showError("Error reading the file. Please try again."); }; reader.readAsArrayBuffer(file); } function extractEmailsFromWorkbook(workbook) { const emails = []; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // Process each sheet workbook.SheetNames.forEach(sheetName => { const worksheet = workbook.Sheets[sheetName]; const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); // Process each row and cell jsonData.forEach(row => { if (Array.isArray(row)) { row.forEach(cell => { if (typeof cell === 'string') { // Check if the cell looks like an email if (emailRegex.test(cell.trim())) { emails.push({ original: cell.trim(), cleaned: cell.trim().toLowerCase(), rowData: row // Store the entire row for later }); } } }); } }); }); return emails; } function initDomainStats(emails) { domainStats = {}; // Count frequency of each domain emails.forEach(email => { const domain = email.cleaned.split('@')[1]; if (!domainStats[domain]) { domainStats[domain] = { count: 0, validMx: 0, invalidMx: 0, deliverable: 0, undeliverable: 0, unknown: 0 }; } domainStats[domain].count++; }); } function processEmails(emails) { let processed = 0; let valid = 0; let issues = 0; let duplicates = 0; let typos = 0; let syntaxErrors = 0; let mxErrors = 0; let undeliverable = 0; let unknown = 0; let deliverable = 0; const totalEmails = emails.length; const emailFrequency = {}; const processedEmailsList = []; // Count frequency for duplicates emails.forEach(email => { const cleanEmail = email.cleaned; emailFrequency[cleanEmail] = (emailFrequency[cleanEmail] || 0) + 1; }); // Process each email emails.forEach((email, index) => { setTimeout(() => { const result = validateEmail(email, emailFrequency); // Update domain stats const domain = email.cleaned.split('@')[1]; if (result.hasMxError) { domainStats[domain].invalidMx++; } else { domainStats[domain].validMx++; if (result.status === 'Valid') { domainStats[domain].deliverable++; deliverable++; } else if (result.isUndeliverable) { domainStats[domain].undeliverable++; } else { domainStats[domain].unknown++; } } processedEmailsList.push({ ...email, ...result }); processed++; // Update counters based on result if (result.status === 'Valid') valid++; else issues++; if (result.isDuplicate) duplicates++; if (result.hasTypo) typos++; if (result.hasSyntaxError) syntaxErrors++; if (result.hasMxError) mxErrors++; if (result.isUndeliverable) undeliverable++; if (result.status === 'Unknown') unknown++; // Update progress const progress = Math.round((processed / totalEmails) * 100); progressBar.style.width = `${progress}%`; progressPercent.textContent = `${progress}%`; processedCount.textContent = processed; validCount.textContent = valid; issuesCount.textContent = issues; // When all emails are processed if (processed === totalEmails) { showResults({ totalEmails, valid, invalid: issues, duplicates, typos, syntaxErrors, mxErrors, undeliverable, unknown, deliverable, processedEmails: processedEmailsList }); } }, index * 10); // Small delay to allow UI updates }); } function validateEmail(email, emailFrequency) { const result = { status: 'Valid', isDuplicate: false, hasTypo: false, hasSyntaxError: false, hasMxError: false, isUndeliverable: false, suggestion: null, details: 'Email appears to be valid', mxResponse: mxResponses.valid, deliverability: deliverabilityMessages.deliverable }; // Check for duplicates if (emailFrequency[email.cleaned] > 1) { result.isDuplicate = true; result.status = 'Duplicate'; result.details = 'This email appears multiple times in your list'; return result; } // Check for syntax errors if (!isValidEmailSyntax(email.original)) { result.hasSyntaxError = true; result.status = 'Invalid Syntax'; result.details = 'Email format is invalid (missing @, invalid characters, etc.)'; result.mxResponse = 'N/A - Invalid email format'; result.deliverability = 'N/A - Invalid email format'; return result; } // Check for typos const typoCheck = checkForTypos(email.cleaned); if (typoCheck.hasTypo) { result.hasTypo = true; result.status = 'Typo'; result.suggestion = typoCheck.suggestion; result.details = `Possible typo detected. Did you mean ${typoCheck.suggestion}?`; } // Simulate MX check (in a real app, this would be an API call) const domain = email.cleaned.split('@')[1]; const mxCheckResult = simulateMxCheck(domain); if (!mxCheckResult.valid) { result.hasMxError = true; result.status = 'Invalid MX'; result.details = 'Domain does not have valid mail server records'; result.mxResponse = mxCheckResult.message; result.deliverability = 'N/A - No mail servers found'; return result; } else { result.mxResponse = mxCheckResult.message; } // Simulate deliverability check (in a real app, this would be an API call) const deliverabilityResult = simulateDeliverabilityCheck(email.cleaned); if (deliverabilityResult.status === 'undeliverable') { result.isUndeliverable = true; result.status = 'Undeliverable'; result.details = 'Email address appears to be invalid or not accepting mail'; result.deliverability = deliverabilityResult.message; } else if (deliverabilityResult.status === 'unknown') { result.status = 'Unknown'; result.details = 'Could not verify deliverability'; result.deliverability = deliverabilityResult.message; } else if (deliverabilityResult.status === 'risky') { result.deliverability = deliverabilityResult.message; } else if (deliverabilityResult.status === 'catchall') { result.deliverability = deliverabilityResult.message; } return result; } function isValidEmailSyntax(email) { // More comprehensive regex for email validation const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return regex.test(email); } function checkForTypos(email) { const result = { hasTypo: false, suggestion: null }; const [localPart, domain] = email.split('@'); // Check for common domain typos if (commonTypos[domain]) { result.hasTypo = true; result.suggestion = `${localPart}@${commonTypos[domain]}`; return result; } // Check for doubled dots if (domain.includes('..')) { const correctedDomain = domain.replace(/\.+/g, '.'); result.hasTypo = true; result.suggestion = `${localPart}@${correctedDomain}`; return result; } // Check for common TLD typos const tld = domain.split('.').pop(); const commonTldTypos = { // Common "com" typos (keyboard proximity, missing letters, repeats) 'con': 'com', 'cpm': 'com', 'cim': 'com', 'col': 'com', 'cok': 'com', 'co': 'com', 'cm': 'com', 'comm': 'com', 'vom': 'com', 'ocm': 'com', 'cmo': 'com', 'comn': 'com', 'coom': 'com', 'commm': 'com', 'c0m': 'com', 'čom': 'com', // Accented character typo 'kom': 'com', // Phonetic error (non-English keyboards) // Common "net" typos 'netl': 'net', 'ner': 'net', 'nte': 'net', 'ent': 'net', 'nett': 'net', 'nettt': 'net', 'n3t': 'net', 'nét': 'net', // Accented typo // Common "org" typos 'orgn': 'org', 'og': 'org', 'ogr': 'org', 'or': 'org', 'rg': 'org', 'orgi': 'org', 'orq': 'org', '0rg': 'org', 'örg': 'org', // Accented/QWERTY errors // Common "gov" typos 'gouv': 'gov', 'gob': 'gov', 'gv': 'gov', 'govv': 'gov', 'gove': 'gov', 'goev': 'gov', 'g0v': 'gov', // Common "edu" typos 'edu.': 'edu', 'eddu': 'edu', 'ed': 'edu', 'eduu': 'edu', '3du': 'edu', // Other generic TLDs 'intl': 'int', 'itn': 'int', 'inr': 'int', 'iint': 'int', // .int typos 'mil.': 'mil', 'mll': 'mil', 'miil': 'mil', // .mil typos 'biz': 'biz', 'bizz': 'biz', 'bzi': 'biz', // .biz typos (prevents overcorrection) 'infoo': 'info', 'infor': 'info', 'infi': 'info', // .info typos // Trailing characters (e.g., accidental punctuation) 'org.': 'org', 'com.': 'com', 'net.': 'net', 'edu..': 'edu', // Country TLD overcorrections (use with caution!) 'uk': 'com', // Common mistake for .uk vs .com 'cm': 'com', // Cameroon’s TLD often mistyped as .com 'om': 'com', // Oman’s TLD mistyped as .com }; if (commonTldTypos[tld]) { const correctedDomain = domain.replace(new RegExp(`${tld}$`), commonTldTypos[tld]); result.hasTypo = true; result.suggestion = `${localPart}@${correctedDomain}`; return result; } return result; } function simulateMxCheck(domain) { // In a real implementation, this would make a DNS query // For simulation, we'll randomly fail for some domains const badDomains = ['example.com', 'test.com', 'invalid.com', 'fakedomain.com']; if (badDomains.includes(domain)) { return { valid: false, message: mxResponses.invalid }; } // Randomly simulate other MX issues const random = Math.random(); if (random < 0.05) { return { valid: false, message: mxResponses.timeout }; } else if (random < 0.1) { return { valid: false, message: mxResponses.restricted }; } else if (random < 0.15) { return { valid: false, message: mxResponses.temporary }; } return { valid: true, message: mxResponses.valid }; } function simulateDeliverabilityCheck(email) { // In a real implementation, this would make an SMTP check // For simulation, we'll randomly mark some as undeliverable const random = Math.random(); if (random < 0.1) { return { status: 'undeliverable', message: deliverabilityMessages.undeliverable }; } else if (random < 0.2) { return { status: 'unknown', message: deliverabilityMessages.unknown }; } else if (random < 0.3) { return { status: 'risky', message: deliverabilityMessages.risky }; } else if (random < 0.4) { return { status: 'catchall', message: deliverabilityMessages.catchall }; } return { status: 'deliverable', message: deliverabilityMessages.deliverable }; } function showResults(stats) { processingSection.classList.add('hidden'); resultsSection.classList.remove('hidden'); // Update summary stats totalEmails.textContent = stats.totalEmails; finalValid.textContent = stats.valid; finalInvalid.textContent = stats.invalid; duplicateCount.textContent = stats.duplicates; typoCount.textContent = stats.typos; syntaxCount.textContent = stats.syntaxErrors; mxCount.textContent = stats.mxErrors; undeliverableCount.textContent = stats.undeliverable; unknownCount.textContent = stats.unknown; // Calculate MX stats const validMx = stats.totalEmails - stats.mxErrors; const validMxPct = Math.round((validMx / stats.totalEmails) * 100); validMxCount.textContent = validMx; validMxPercent.textContent = validMxPct; invalidMxCount.textContent = stats.mxErrors; // Show top MX issues const problematicDomains = Object.entries(domainStats) .filter(([domain, stats]) => stats.invalidMx > 0) .sort((a, b) => b[1].invalidMx - a[1].invalidMx) .slice(0, 5); if (problematicDomains.length > 0) { topMxIssues.innerHTML = problematicDomains .map(([domain, stats]) => `<div class="flex justify-between py-1"> <span class="truncate">${domain}</span> <span class="font-medium">${stats.invalidMx} issues</span> </div>` ).join(''); } else { topMxIssues.textContent = "No significant MX issues detected"; } // Update deliverability stats deliverableCount.textContent = stats.deliverable; const deliverablePct = Math.round((stats.deliverable / stats.totalEmails) * 100); deliverablePercent.textContent = deliverablePct; undeliverableInsightCount.textContent = stats.undeliverable; unknownDeliverabilityCount.textContent = stats.unknown; // Calculate deliverability score (0-100) const deliverabilityScoreValue = Math.min(100, Math.max(0, (stats.deliverable / stats.totalEmails) * 80 + ((stats.totalEmails - stats.undeliverable) / stats.totalEmails) * 20 )); deliverabilityScoreBar.style.width = `${deliverabilityScoreValue}%`; deliverabilityScore.textContent = Math.round(deliverabilityScoreValue); // Set deliverability rating if (deliverabilityScoreValue >= 80) { deliverabilityRating.textContent = "Excellent"; deliverabilityRating.className = "font-medium text-green-600"; deliverabilityDescription.textContent = "This list has excellent deliverability potential"; } else if (deliverabilityScoreValue >= 60) { deliverabilityRating.textContent = "Good"; deliverabilityRating.className = "font-medium text-blue-600"; deliverabilityDescription.textContent = "This list has good deliverability with minor issues"; } else if (deliverabilityScoreValue >= 40) { deliverabilityRating.textContent = "Fair"; deliverabilityRating.className = "font-medium text-yellow-600"; deliverabilityDescription.textContent = "This list may have deliverability issues that need attention"; } else { deliverabilityRating.textContent = "Poor"; deliverabilityRating.className = "font-medium text-red-600"; deliverabilityDescription.textContent = "This list has significant deliverability issues that need attention"; } // Show first 100 emails in the table const emailsToShow = stats.processedEmails.slice(0, 100); showingCount.textContent = emailsToShow.length; totalCount.textContent = stats.totalEmails; emailTableBody.innerHTML = ''; emailsToShow.forEach(email => { const row = document.createElement('tr'); row.className = 'fade-in'; // Status badge let statusClass = ''; if (email.status === 'Valid') statusClass = 'status-valid'; else if (email.status === 'Duplicate') statusClass = 'status-duplicate'; else if (email.status === 'Typo') statusClass = 'status-typo'; else if (email.status === 'Invalid Syntax') statusClass = 'status-invalid'; else if (email.status === 'Invalid MX') statusClass = 'status-mx'; else if (email.status === 'Undeliverable') statusClass = 'status-undeliverable'; else statusClass = 'status-unknown'; // Tooltip for details const tooltipId = `tooltip-${Math.random().toString(36).substr(2, 9)}`; row.innerHTML = ` <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-800">${email.original}</td> <td class="px-4 py-3 whitespace-nowrap"> <span class="status-badge ${statusClass}">${email.status}</span> </td> <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-600"> <div class="tooltip"> <i class="fas fa-info-circle text-gray-400"></i> <span class="tooltip-text">${email.details}${email.suggestion ? `<br><br>Suggestion: ${email.suggestion}` : ''}</span> </div> ${email.suggestion ? `<span class="ml-2">${email.suggestion}</span>` : ''} </td> <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-600"> <div class="tooltip"> <i class="fas fa-server text-gray-400"></i> <span class="tooltip-text">${email.mxResponse}</span> </div> <span class="ml-2">${email.deliverability}</span> </td> `; emailTableBody.appendChild(row); }); // Store processed emails for download processedEmails = stats.processedEmails; workbook = addValidationResultsToWorkbook(workbook, processedEmails); } function addValidationResultsToWorkbook(workbook, processedEmails) { // Create a map of original emails to their validation results const emailMap = {}; processedEmails.forEach(email => { emailMap[email.original] = { status: email.status, suggestion: email.suggestion || '', mxResponse: email.mxResponse, deliverability: email.deliverability }; }); // Process each sheet workbook.SheetNames.forEach(sheetName => { const worksheet = workbook.Sheets[sheetName]; const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); // Add headers for new columns if they don't exist if (jsonData.length > 0) { const headers = jsonData[0]; if (!headers.includes('Email Status')) { headers.push('Email Status'); headers.push('Suggestion'); headers.push('MX Response'); headers.push('Deliverability'); } } // Process each row for (let i = 1; i < jsonData.length; i++) { const row = jsonData[i]; if (!Array.isArray(row)) continue; // Find email in row let emailInRow = null; for (let j = 0; j < row.length; j++) { const cell = row[j]; if (typeof cell === 'string' && isValidEmailSyntax(cell.trim())) { emailInRow = cell.trim(); break; } } // Add validation results if (emailInRow && emailMap[emailInRow]) { // Make sure row has enough columns while (row.length < jsonData[0].length - 4) { row.push(''); } row.push(emailMap[emailInRow].status); row.push(emailMap[emailInRow].suggestion); row.push(emailMap[emailInRow].mxResponse); row.push(emailMap[emailInRow].deliverability); } else { // For rows without email or invalid email while (row.length < jsonData[0].length - 4) { row.push(''); } row.push('No valid email'); row.push(''); row.push(''); row.push(''); } } // Convert back to worksheet const newWorksheet = XLSX.utils.aoa_to_sheet(jsonData); workbook.Sheets[sheetName] = newWorksheet; }); return workbook; } function downloadCleanedFile() { if (!workbook) return; // Convert workbook to binary const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); // Create blob and download const blob = new Blob([wbout], { type: 'application/octet-stream' }); saveAs(blob, 'cleaned_emails.xlsx'); } function downloadSummaryReport() { // In a real implementation, this would generate a PDF report // For this demo, we'll create a more detailed text file let reportText = `EMAIL VALIDATION REPORT\n`; reportText += `================================\n`; reportText += `Generated on: ${new Date().toLocaleString()}\n\n`; // Summary stats reportText += `SUMMARY STATISTICS\n`; reportText += `------------------\n`; reportText += `Total Emails Processed: ${totalEmails.textContent}\n`; reportText += `Valid Emails: ${finalValid.textContent} (${Math.round((finalValid.textContent / totalEmails.textContent) * 100)}%)\n`; reportText += `Invalid Emails: ${finalInvalid.textContent} (${Math.round((finalInvalid.textContent / totalEmails.textContent) * 100)}%)\n\n`; // Detailed issues reportText += `DETAILED ISSUES\n`; reportText += `---------------\n`; reportText += `- Duplicates: ${duplicateCount.textContent}\n`; reportText += `- Typos Found: ${typoCount.textContent}\n`; reportText += `- Syntax Errors: ${syntaxCount.textContent}\n`; reportText += `- MX Record Issues: ${mxCount.textContent}\n`; reportText += `- Undeliverable: ${undeliverableCount.textContent}\n`; reportText += `- Unknown Status: ${unknownCount.textContent}\n\n`; // MX Analysis reportText += `MX RECORD ANALYSIS\n`; reportText += `------------------\n`; reportText += `Emails with valid MX records: ${validMxCount.textContent} (${validMxPercent.textContent}%)\n`; reportText += `Emails with MX issues: ${invalidMxCount.textContent}\n\n`; // Top MX issues const topMxDomains = document.querySelectorAll('#top-mx-issues div'); if (topMxDomains.length > 0) { reportText += `Top Domains with MX Issues:\n`; topMxDomains.forEach(domain => { reportText += ` - ${domain.textContent.trim()}\n`; }); reportText += `\n`; } // Deliverability Analysis reportText += `DELIVERABILITY ANALYSIS\n`; reportText += `-----------------------\n`; reportText += `Confirmed Deliverable: ${deliverableCount.textContent} (${deliverablePercent.textContent}%)\n`; reportText += `Undeliverable: ${undeliverableInsightCount.textContent}\n`; reportText += `Unknown Status: ${unknownDeliverabilityCount.textContent}\n\n`; reportText += `Overall Deliverability Score: ${deliverabilityScore.textContent}/100\n`; reportText += `Rating: ${deliverabilityRating.textContent}\n`; reportText += `Description: ${deliverabilityDescription.textContent}\n\n`; // Recommendations reportText += `RECOMMENDATIONS\n`; reportText += `---------------\n`; if (parseInt(mxCount.textContent) > 0) { reportText += `1. Address MX record issues for ${mxCount.textContent} emails to improve deliverability\n`; } if (parseInt(typoCount.textContent) > 0) { reportText += `2. Review ${typoCount.textContent} emails with potential typos for correction\n`; } if (parseInt(duplicateCount.textContent) > 0) { reportText += `3. Remove ${duplicateCount.textContent} duplicate emails to maintain list quality\n`; } if (parseInt(undeliverableCount.textContent) > 0) { reportText += `4. Remove or verify ${undeliverableCount.textContent} undeliverable emails to improve sender reputation\n`; } const blob = new Blob([reportText], { type: 'text/plain;charset=utf-8' }); saveAs(blob, 'email_validation_report.txt'); } function showError(message) { uploadSection.classList.add('hidden'); processingSection.classList.add('hidden'); resultsSection.classList.add('hidden'); errorSection.classList.remove('hidden'); document.getElementById('error-message').textContent = message; } function resetForm() { uploadSection.classList.remove('hidden'); processingSection.classList.add('hidden'); resultsSection.classList.add('hidden'); errorSection.classList.add('hidden'); // Reset progress progressBar.style.width = '0%'; progressPercent.textContent = '0%'; processedCount.textContent = '0'; validCount.textContent = '0'; issuesCount.textContent = '0'; // Reset file input fileUpload.value = ''; // Clear data emailData = []; processedEmails = []; workbook = null; domainStats = {}; } }); </script> <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sqibhe/email-validation" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced Email Validation Bot</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.dropzone {
border: 2px dashed #4b5563;
border-radius: 0.5rem;
transition: all 0.3s ease;
}
.dropzone.active {
border-color: #3b82f6;
background-color: rgba(59, 130, 246, 0.05);
}
.progress-bar {
transition: width 0.3s ease;
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltip-text {
visibility: hidden;
width: 200px;
background-color: #1f2937;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
.status-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.status-valid {
background-color: #d1fae5;
color: #065f46;
}
.status-invalid {
background-color: #fee2e2;
color: #b91c1c;
}
.status-duplicate {
background-color: #fef3c7;
color: #92400e;
}
.status-typo {
background-color: #dbeafe;
color: #1e40af;
}
.status-unknown {
background-color: #e5e7eb;
color: #4b5563;
}
.status-undeliverable {
background-color: #f5f5f4;
color: #57534e;
}
.status-mx {
background-color: #ede9fe;
color: #5b21b6;
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.insight-card {
transition: all 0.3s ease;
}
.insight-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-6xl">