Skip to content

Commit 3f1eb92

Browse files
authored
Merge pull request #3 from imprvhub/2-two-letter-domains-that-are-not-possible-are-shown-as-available
fix: Validate domain length per TLD
2 parents 2f3d405 + e33be47 commit 3f1eb92

File tree

1 file changed

+74
-5
lines changed

1 file changed

+74
-5
lines changed

src/mcp_domain_availability/main.py

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,43 @@
4141

4242
ALL_TLDS = list(set(POPULAR_TLDS + COUNTRY_TLDS + NEW_TLDS))
4343

44+
TLD_MIN_LENGTH = {
45+
"com": 3, "net": 3, "org": 3, "info": 3, "biz": 3,
46+
"io": 3, "ai": 3, "co": 3, "me": 3,
47+
"de": 3, "fr": 3, "it": 3, "es": 3, "nl": 3,
48+
"ch": 3, "at": 3, "be": 3, "dk": 3, "se": 3,
49+
"no": 3, "fi": 3, "pl": 3, "cz": 3, "pt": 3,
50+
"gr": 3, "tr": 3, "ru": 3, "uk": 3, "au": 3,
51+
"ca": 3, "us": 3, "jp": 3, "kr": 3, "cn": 3,
52+
"in": 3, "br": 3, "mx": 3, "ar": 3, "cl": 3,
53+
"pe": 3, "za": 3, "eg": 3, "ma": 3, "ng": 3,
54+
"ke": 3,
55+
56+
"app": 3, "dev": 3, "xyz": 3, "tech": 3,
57+
"online": 3, "site": 3, "website": 3, "store": 3,
58+
"shop": 3, "cloud": 3, "digital": 3, "blog": 3,
59+
"news": 3
60+
}
61+
62+
def get_min_length_for_tld(tld: str) -> int:
63+
"""Obtiene la longitud mínima requerida para un TLD"""
64+
return TLD_MIN_LENGTH.get(tld, 3)
65+
66+
def is_valid_domain_name(base_name: str, tld: str) -> bool:
67+
"""Valida si un nombre de dominio cumple con las reglas del TLD"""
68+
min_length = get_min_length_for_tld(tld)
69+
if len(base_name) < min_length:
70+
return False
71+
if not re.match(r'^[a-z0-9]([a-z0-9-]*[a-z0-9])?$', base_name):
72+
return False
73+
if base_name.startswith('-') or base_name.endswith('-'):
74+
return False
75+
if '--' in base_name:
76+
return False
77+
if len(base_name) > 63:
78+
return False
79+
return True
80+
4481
def clean_domain_name(domain: str) -> str:
4582
domain = domain.lower().strip()
4683
if domain.startswith('http://') or domain.startswith('https://'):
@@ -136,6 +173,15 @@ def socket_check():
136173

137174
async def check_domain_availability(domain: str) -> Dict:
138175
start_time = time.time()
176+
base_name, tld = extract_domain_parts(domain)
177+
if not is_valid_domain_name(base_name, tld):
178+
return {
179+
'domain': domain,
180+
'available': False,
181+
'error': f'Invalid domain: "{base_name}" does not meet requirements for .{tld} (min length: {get_min_length_for_tld(tld)})',
182+
'valid': False,
183+
'check_time': "0s"
184+
}
139185

140186
dns_available = await check_domain_dns(domain)
141187
whois_available = await check_domain_whois(domain)
@@ -150,6 +196,7 @@ async def check_domain_availability(domain: str) -> Dict:
150196
'available': is_available,
151197
'dns_available': dns_available,
152198
'whois_available': whois_available,
199+
'valid': True,
153200
'check_time': f"{check_time}s"
154201
}
155202

@@ -158,8 +205,11 @@ async def check_multiple_domains(base_name: str, tlds: List[str]) -> List[Dict]:
158205

159206
async def check_with_semaphore(tld: str):
160207
async with semaphore:
161-
domain = f"{base_name}.{tld}"
162-
return await check_domain_availability(domain)
208+
if is_valid_domain_name(base_name, tld):
209+
domain = f"{base_name}.{tld}"
210+
return await check_domain_availability(domain)
211+
else:
212+
return None
163213

164214
tasks = [check_with_semaphore(tld) for tld in tlds]
165215
results = await asyncio.gather(*tasks, return_exceptions=True)
@@ -168,7 +218,7 @@ async def check_with_semaphore(tld: str):
168218
for result in results:
169219
if isinstance(result, dict):
170220
valid_results.append(result)
171-
else:
221+
elif result is not None and not isinstance(result, Exception):
172222
print(f"Error checking domain: {result}")
173223

174224
return valid_results
@@ -180,22 +230,40 @@ async def run_domain_checks(domain_part: str) -> Dict:
180230
"requested_domain": None,
181231
"available_domains": [],
182232
"unavailable_domains": [],
233+
"invalid_domains": [],
183234
"total_checked": 0,
184235
"check_summary": {}
185236
}
186237

238+
invalid_for_tlds = []
239+
for tld in ALL_TLDS:
240+
if not is_valid_domain_name(base_name, tld):
241+
min_length = get_min_length_for_tld(tld)
242+
invalid_for_tlds.append({
243+
'tld': tld,
244+
'reason': f'Minimum length: {min_length} chars'
245+
})
246+
247+
if invalid_for_tlds:
248+
results["invalid_domains"] = {
249+
'base_name': base_name,
250+
'length': len(base_name),
251+
'invalid_for': invalid_for_tlds[:10]
252+
}
253+
187254
if existing_tld:
188255
exact_result = await check_domain_availability(f"{base_name}.{existing_tld}")
189256
results["requested_domain"] = exact_result
190257

191258
other_tlds = [tld for tld in ALL_TLDS if tld != existing_tld]
192259
all_results = await check_multiple_domains(base_name, other_tlds)
193-
all_results.append(exact_result)
260+
if exact_result.get('valid', True):
261+
all_results.append(exact_result)
194262
else:
195263
all_results = await check_multiple_domains(base_name, ALL_TLDS)
196264

197265
for result in all_results:
198-
if result['available']:
266+
if result.get('available'):
199267
results["available_domains"].append(result)
200268
else:
201269
results["unavailable_domains"].append(result)
@@ -210,6 +278,7 @@ async def run_domain_checks(domain_part: str) -> Dict:
210278
results["check_summary"] = {
211279
"total_available": len(results["available_domains"]),
212280
"total_unavailable": len(results["unavailable_domains"]),
281+
"total_invalid": len(invalid_for_tlds),
213282
"popular_available": len(popular_available),
214283
"country_available": len([r for r in results["available_domains"]
215284
if any(r['domain'].endswith(f'.{tld}') for tld in COUNTRY_TLDS)]),

0 commit comments

Comments
 (0)