Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
# Email-Variations-Generator
Generate variation of duplicate valid Email for your email address

**Live demo:** https://fea-email-variations-generator.vercel.app/

## How it works

Gmail ignores dots (`.`) in the username part of an address, so
`johndoe@gmail.com`, `john.doe@gmail.com` and `j.o.h.n.d.o.e@gmail.com` all
deliver to the same inbox. This tool lists every way of placing dots in your
username, which is handy for sign-up testing, filtering, or spotting where an
address was shared.

- Existing dots are ignored, so `john.doe@gmail.com` and `johndoe@gmail.com`
give the same 64 variations (never `john..doe`).
- A `+tag` suffix (e.g. `john+news@gmail.com`) is kept unchanged; only the
part before `+` is varied.
- A username of *n* characters has 2^(n-1) variations. The output is capped at
8,192 (every variation for usernames up to 14 characters) to keep the page
responsive; the page tells you when results are truncated.
- Results can be exported to an `.xlsx` file (via [SheetJS](https://sheetjs.com/)).

> Dot-insensitivity is a Gmail / Google Workspace behavior. Most other
> providers treat dotted addresses as different mailboxes.

## Run locally

It's a static page with no build step: open `index.html` in a browser, or
serve the folder (e.g. `npx serve .`).

## License

[MIT](LICENSE)
24 changes: 14 additions & 10 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,33 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Variations Generator</title>
<meta name="description" content="Generate Gmail dot-variations of your email address (e.g. j.ohn.doe@gmail.com) and export them to Excel.">
<link rel="stylesheet" href="style.css">
</head>

<body>
<h2>Email Variations Generator</h2>
<h2> FEA in the url is an abbr for : <a href="https://fadyehabamer.com"> Fady Ehab Amer </a> </h3>
<h1>Email Variations Generator</h1>
<p class="subtitle">FEA in the URL is an abbreviation for: <a href="https://fadyehabamer.com">Fady Ehab Amer</a></p>
<div>
<label for="emailInput">Enter Email:</label>
<input type="email" id="emailInput">
<section class="controls">
<button onclick="generateAndDisplayVariations()">Generate Variations</button>
<button id="exportBtn" onclick="exportToExcel()" disabled>Export to Excel</button>
</section>
<form novalidate onsubmit="event.preventDefault(); generateAndDisplayVariations();">
<label for="emailInput">Enter Email:</label>
<input type="email" id="emailInput" autocomplete="email" aria-describedby="errorMsg">
<section class="controls">
<button type="submit">Generate Variations</button>
<button type="button" id="exportBtn" onclick="exportToExcel()" disabled>Export to Excel</button>
</section>
</form>
</div>

<span id="errorMsg"></span>
<p id="errorMsg" role="alert"></p>
<p id="statusMsg" role="status"></p>

<table id="results">
<!-- The results will be displayed here in table format -->
</table>


<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.0/xlsx.full.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
<script src="main.js"></script>
</body>

Expand Down
66 changes: 47 additions & 19 deletions main.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
function generateEmailVariations(email) {
// The number of variations doubles with every character of the username
// (2^(n-1)), so a long username would freeze the page. Cap the output; 8192
// covers every variation of usernames up to 14 characters.
const MAX_VARIATIONS = 8192;

// Total number of dot variations for an email (may exceed MAX_VARIATIONS).
function countEmailVariations(email) {
let localPart = email.split("@")[0].split("+")[0].replace(/\./g, "");
return 2 ** Math.max(localPart.length - 1, 0);
}

function generateEmailVariations(email, limit = MAX_VARIATIONS) {
let [localPart, domain] = email.split("@");

// Gmail ignores dots in the username, so start from the dot-free form.
// Otherwise existing dots get combined with inserted ones and produce
// invalid addresses such as "john..doe@gmail.com".
// A "+tag" suffix is kept as-is; only the username before it is varied.
let plusIndex = localPart.indexOf("+");
let tag = plusIndex === -1 ? "" : localPart.slice(plusIndex);
localPart = (plusIndex === -1 ? localPart : localPart.slice(0, plusIndex)).replace(/\./g, "");

let variations = new Set();

function addDots(prefix, remaining) {
if (variations.size >= limit) return;

if (!remaining) {
variations.add(prefix + "@" + domain);
variations.add(prefix + tag + "@" + domain);
return;
}

Expand All @@ -23,29 +44,34 @@ function generateEmailVariations(email) {
return Array.from(variations);
}

let previouslyGeneratedEmail = '';
// Email and variations currently shown in the table (used by the export).
let currentEmail = '';
let currentVariations = [];

function generateAndDisplayVariations() {
let emailInput = document.getElementById('emailInput');
let email = emailInput.value;
let email = emailInput.value.trim();
let errorMsg = document.getElementById('errorMsg');
let statusMsg = document.getElementById('statusMsg');
let exportBtn = document.getElementById('exportBtn');


// Email validation
if (!validateEmail(email)) {
alert("Please enter a valid email address!");
return;
}

// Check if the email is the same as the previously generated one
if (email === previouslyGeneratedEmail) {
alert("This email has already been generated!");
errorMsg.textContent = "Please enter a valid email address.";
emailInput.setAttribute('aria-invalid', 'true');
emailInput.focus();
return;
}
errorMsg.textContent = '';
emailInput.removeAttribute('aria-invalid');

let resultsTable = document.getElementById('results');
let validEmails = generateEmailVariations(email);
let total = countEmailVariations(email);
statusMsg.textContent = total > validEmails.length
? `Showing the first ${validEmails.length.toLocaleString()} of ${total.toLocaleString()} possible variations.`
: `${validEmails.length.toLocaleString()} variations generated.`;

// Clear previous results
resultsTable.innerHTML = '';
Expand All @@ -65,21 +91,23 @@ function generateAndDisplayVariations() {
// Enable the export button
exportBtn.disabled = false;

// Set this email as the previously generated one
previouslyGeneratedEmail = email;

// Disable the email input to prevent changing the email
emailInput.disabled = true;
currentEmail = email;
currentVariations = validEmails;
}

function validateEmail(email) {
var re = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
// Local part: dot-separated atoms (no leading, trailing or double dots),
// "+" allowed for tags. Domain: dot-separated labels that don't start or
// end with "-", followed by a TLD of 2+ letters (e.g. ".photography").
var re = /^[a-zA-Z0-9_%+-]+(\.[a-zA-Z0-9_%+-]+)*@([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
return re.test(email);
}

function exportToExcel() {
let email = document.getElementById('emailInput').value;
let validEmails = generateEmailVariations(email);
// Export exactly what is displayed, even if the input was edited since.
let email = currentEmail;
let validEmails = currentVariations;
if (!validEmails.length) return;

let wb = XLSX.utils.book_new();
let ws_name = "EmailVariations";
Expand Down
8 changes: 7 additions & 1 deletion style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ body {
background-color: #f4f4f4;
}

h2 {
h1,
.subtitle {
text-align: center;
color: #333;
}
Expand Down Expand Up @@ -81,3 +82,8 @@ td {
.controls button {
flex-grow: 1;
}

#errorMsg {
color: #b00020;
font-weight: bold;
}
Loading