Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
Adds HUNT (Sniper/Trend/Forge), CFO dashboard (burn rate + kill list), and a plugin-based Analyze side panel with caching and SSRF hardening.
90 lines
1.9 KiB
Python
90 lines
1.9 KiB
Python
"""
|
|
Typo generator for Trend Surfer / brand typos.
|
|
|
|
No external APIs.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import string
|
|
|
|
|
|
KEYBOARD_NEIGHBORS = {
|
|
# simplified QWERTY adjacency (enough for useful typos)
|
|
"q": "wa",
|
|
"w": "qase",
|
|
"e": "wsdr",
|
|
"r": "edft",
|
|
"t": "rfgy",
|
|
"y": "tghu",
|
|
"u": "yhji",
|
|
"i": "ujko",
|
|
"o": "iklp",
|
|
"p": "ol",
|
|
"a": "qwsz",
|
|
"s": "awedxz",
|
|
"d": "serfcx",
|
|
"f": "drtgvc",
|
|
"g": "ftyhbv",
|
|
"h": "gyujbn",
|
|
"j": "huikmn",
|
|
"k": "jiolm",
|
|
"l": "kop",
|
|
"z": "asx",
|
|
"x": "zsdc",
|
|
"c": "xdfv",
|
|
"v": "cfgb",
|
|
"b": "vghn",
|
|
"n": "bhjm",
|
|
"m": "njk",
|
|
}
|
|
|
|
|
|
def _normalize_brand(brand: str) -> str:
|
|
b = (brand or "").lower().strip()
|
|
b = "".join(ch for ch in b if ch in string.ascii_lowercase)
|
|
return b
|
|
|
|
|
|
def generate_typos(brand: str, *, limit: int = 100) -> list[str]:
|
|
b = _normalize_brand(brand)
|
|
if len(b) < 2:
|
|
return []
|
|
|
|
candidates: list[str] = []
|
|
seen = set()
|
|
|
|
def _add(s: str):
|
|
if s and s not in seen and s != b:
|
|
seen.add(s)
|
|
candidates.append(s)
|
|
|
|
# 1) single deletion
|
|
for i in range(len(b)):
|
|
_add(b[:i] + b[i + 1 :])
|
|
if len(candidates) >= limit:
|
|
return candidates
|
|
|
|
# 2) single insertion (duplicate char)
|
|
for i in range(len(b)):
|
|
_add(b[:i] + b[i] + b[i:])
|
|
if len(candidates) >= limit:
|
|
return candidates
|
|
|
|
# 3) adjacent transposition
|
|
for i in range(len(b) - 1):
|
|
_add(b[:i] + b[i + 1] + b[i] + b[i + 2 :])
|
|
if len(candidates) >= limit:
|
|
return candidates
|
|
|
|
# 4) neighbor substitution
|
|
for i, ch in enumerate(b):
|
|
neigh = KEYBOARD_NEIGHBORS.get(ch, "")
|
|
for n in neigh:
|
|
_add(b[:i] + n + b[i + 1 :])
|
|
if len(candidates) >= limit:
|
|
return candidates
|
|
|
|
return candidates[:limit]
|
|
|