Skip to content

Commit ffd5d2b

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

File tree

1 file changed

+78
-5
lines changed

1 file changed

+78
-5
lines changed

src/mcp_domain_availability/main.py

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

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

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

179+
base_name, tld = extract_domain_parts(domain)
180+
181+
if not is_valid_domain_name(base_name, tld):
182+
return {
183+
'domain': domain,
184+
'available': False,
185+
'error': f'Invalid domain: "{base_name}" does not meet requirements for .{tld} (min length: {get_min_length_for_tld(tld)})',
186+
'valid': False,
187+
'check_time': "0s"
188+
}
189+
140190
dns_available = await check_domain_dns(domain)
141191
whois_available = await check_domain_whois(domain)
142192

@@ -150,6 +200,7 @@ async def check_domain_availability(domain: str) -> Dict:
150200
'available': is_available,
151201
'dns_available': dns_available,
152202
'whois_available': whois_available,
203+
'valid': True,
153204
'check_time': f"{check_time}s"
154205
}
155206

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

159210
async def check_with_semaphore(tld: str):
160211
async with semaphore:
161-
domain = f"{base_name}.{tld}"
162-
return await check_domain_availability(domain)
212+
if is_valid_domain_name(base_name, tld):
213+
domain = f"{base_name}.{tld}"
214+
return await check_domain_availability(domain)
215+
else:
216+
return None
163217

164218
tasks = [check_with_semaphore(tld) for tld in tlds]
165219
results = await asyncio.gather(*tasks, return_exceptions=True)
@@ -168,7 +222,7 @@ async def check_with_semaphore(tld: str):
168222
for result in results:
169223
if isinstance(result, dict):
170224
valid_results.append(result)
171-
else:
225+
elif result is not None and not isinstance(result, Exception):
172226
print(f"Error checking domain: {result}")
173227

174228
return valid_results
@@ -180,22 +234,40 @@ async def run_domain_checks(domain_part: str) -> Dict:
180234
"requested_domain": None,
181235
"available_domains": [],
182236
"unavailable_domains": [],
237+
"invalid_domains": [],
183238
"total_checked": 0,
184239
"check_summary": {}
185240
}
186241

242+
invalid_for_tlds = []
243+
for tld in ALL_TLDS:
244+
if not is_valid_domain_name(base_name, tld):
245+
min_length = get_min_length_for_tld(tld)
246+
invalid_for_tlds.append({
247+
'tld': tld,
248+
'reason': f'Minimum length: {min_length} chars'
249+
})
250+
251+
if invalid_for_tlds:
252+
results["invalid_domains"] = {
253+
'base_name': base_name,
254+
'length': len(base_name),
255+
'invalid_for': invalid_for_tlds[:10]
256+
}
257+
187258
if existing_tld:
188259
exact_result = await check_domain_availability(f"{base_name}.{existing_tld}")
189260
results["requested_domain"] = exact_result
190261

191262
other_tlds = [tld for tld in ALL_TLDS if tld != existing_tld]
192263
all_results = await check_multiple_domains(base_name, other_tlds)
193-
all_results.append(exact_result)
264+
if exact_result.get('valid', True):
265+
all_results.append(exact_result)
194266
else:
195267
all_results = await check_multiple_domains(base_name, ALL_TLDS)
196268

197269
for result in all_results:
198-
if result['available']:
270+
if result.get('available'):
199271
results["available_domains"].append(result)
200272
else:
201273
results["unavailable_domains"].append(result)
@@ -210,6 +282,7 @@ async def run_domain_checks(domain_part: str) -> Dict:
210282
results["check_summary"] = {
211283
"total_available": len(results["available_domains"]),
212284
"total_unavailable": len(results["unavailable_domains"]),
285+
"total_invalid": len(invalid_for_tlds),
213286
"popular_available": len(popular_available),
214287
"country_available": len([r for r in results["available_domains"]
215288
if any(r['domain'].endswith(f'.{tld}') for tld in COUNTRY_TLDS)]),

0 commit comments

Comments
 (0)