117 lines
2.6 KiB
Python
117 lines
2.6 KiB
Python
"""
|
|
Pounce Score calculation.
|
|
|
|
Used across:
|
|
- Market feed scoring
|
|
- Auction scraper (persist score for DB-level sorting)
|
|
- Listings (optional)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
|
|
def calculate_pounce_score_v2(
|
|
domain: str,
|
|
tld: Optional[str] = None,
|
|
*,
|
|
num_bids: int = 0,
|
|
age_years: int = 0,
|
|
is_pounce: bool = False,
|
|
) -> int:
|
|
"""
|
|
Pounce Score v2.0 - Enhanced scoring algorithm.
|
|
|
|
Factors:
|
|
- Length (shorter = more valuable)
|
|
- TLD premium
|
|
- Market activity (bids)
|
|
- Age bonus
|
|
- Pounce Direct bonus (verified listings)
|
|
- Penalties (hyphens, numbers, etc.)
|
|
"""
|
|
score = 50 # Baseline
|
|
|
|
domain = (domain or "").strip().lower()
|
|
if not domain:
|
|
return score
|
|
|
|
name = domain.rsplit(".", 1)[0] if "." in domain else domain
|
|
tld_clean = (tld or (domain.rsplit(".", 1)[-1] if "." in domain else "")).strip().lower().lstrip(".")
|
|
|
|
# A) LENGTH BONUS (exponential for short domains)
|
|
length_scores = {1: 50, 2: 45, 3: 40, 4: 30, 5: 20, 6: 15, 7: 10}
|
|
score += length_scores.get(len(name), max(0, 15 - len(name)))
|
|
|
|
# B) TLD PREMIUM
|
|
tld_scores = {
|
|
"com": 20,
|
|
"ai": 25,
|
|
"io": 18,
|
|
"co": 12,
|
|
"ch": 15,
|
|
"de": 10,
|
|
"net": 8,
|
|
"org": 8,
|
|
"app": 10,
|
|
"dev": 10,
|
|
"xyz": 5,
|
|
}
|
|
score += tld_scores.get(tld_clean, 0)
|
|
|
|
# C) MARKET ACTIVITY (bids = demand signal)
|
|
try:
|
|
bids = int(num_bids or 0)
|
|
except Exception:
|
|
bids = 0
|
|
if bids >= 20:
|
|
score += 15
|
|
elif bids >= 10:
|
|
score += 10
|
|
elif bids >= 5:
|
|
score += 5
|
|
elif bids >= 2:
|
|
score += 2
|
|
|
|
# D) AGE BONUS (established domains)
|
|
try:
|
|
age = int(age_years or 0)
|
|
except Exception:
|
|
age = 0
|
|
if age > 15:
|
|
score += 10
|
|
elif age > 10:
|
|
score += 7
|
|
elif age > 5:
|
|
score += 3
|
|
|
|
# E) POUNCE DIRECT BONUS (verified = trustworthy)
|
|
if is_pounce:
|
|
score += 10
|
|
|
|
# F) PENALTIES
|
|
if "-" in name:
|
|
score -= 25
|
|
if any(c.isdigit() for c in name) and len(name) > 3:
|
|
score -= 20
|
|
if len(name) > 15:
|
|
score -= 15
|
|
|
|
# G) CONSONANT CHECK (no gibberish like "xkqzfgh")
|
|
consonants = "bcdfghjklmnpqrstvwxyz"
|
|
max_streak = 0
|
|
current_streak = 0
|
|
for c in name.lower():
|
|
if c in consonants:
|
|
current_streak += 1
|
|
max_streak = max(max_streak, current_streak)
|
|
else:
|
|
current_streak = 0
|
|
if max_streak > 4:
|
|
score -= 15
|
|
|
|
return max(0, min(100, score))
|
|
|
|
|