18 Commits
Author SHA1 Message Date
klemek 10daddb54c chore: 1.4.2
Python Lint CI / ruff (push) Successful in 13m4s
Python Lint CI / ruff-format-check (push) Successful in 13m9s
Python Lint CI / ty (push) Successful in 12m32s
2026-06-16 11:31:53 +02:00
klemek d866b8fbb2 fix: invalid chars in french.txt 2026-06-16 11:31:50 +02:00
klemek 61086e0b9b chore: 1.4.1
Python Lint CI / ty (push) Successful in 1m59s
Python Lint CI / ruff-format-check (push) Successful in 1m59s
Python Lint CI / ruff (push) Successful in 7m18s
2026-06-16 11:09:29 +02:00
klemek ce53920340 fix: use symlink for txt files
Python Lint CI / ruff-format-check (push) Has been cancelled
Python Lint CI / ty (push) Has been cancelled
Python Lint CI / ruff (push) Has been cancelled
2026-06-16 11:08:33 +02:00
klemek 78c5c56220 chore: 1.4.0
Python Lint CI / ruff (push) Successful in 1m36s
Python Lint CI / ruff-format-check (push) Successful in 3m40s
Python Lint CI / ty (push) Successful in 3m41s
2026-06-16 10:56:16 +02:00
klemek 873303661a fix: use fixed tld list 2026-06-16 10:56:12 +02:00
klemek 0a8775dfc9 chore: 1.3.0
Python Lint CI / ty (push) Successful in 4m5s
Python Lint CI / ruff-format-check (push) Successful in 4m6s
Python Lint CI / ruff (push) Successful in 55s
2026-06-16 00:24:47 +02:00
klemek 015eebd6ed feat: blacklist 2026-06-16 00:24:40 +02:00
klemek 3df8cfffc7 chore: 1.2.0
Python Lint CI / ruff-format-check (push) Successful in 2m32s
Python Lint CI / ty (push) Successful in 3m7s
Python Lint CI / ruff (push) Successful in 1m5s
2026-06-15 15:09:33 +02:00
klemek 85a1c47c8c feat: custom word list 2026-06-15 15:09:27 +02:00
klemek f91384173b chore: 1.1.2
Python Lint CI / ruff (push) Successful in 1m39s
Python Lint CI / ruff-format-check (push) Successful in 4m16s
Python Lint CI / ty (push) Successful in 2m59s
2026-06-15 14:43:55 +02:00
klemek f253115e62 chore: lower base python
Python Lint CI / ruff (push) Has been cancelled
Python Lint CI / ruff-format-check (push) Has been cancelled
Python Lint CI / ty (push) Has been cancelled
2026-06-15 14:43:24 +02:00
klemek 75c96efc76 tools(make): release target 2026-06-15 14:40:07 +02:00
klemek 15a937e39c chore: version 1.1.1
Python Lint CI / ruff (push) Successful in 2m30s
Python Lint CI / ruff-format-check (push) Successful in 3m3s
Python Lint CI / ty (push) Successful in 3m6s
2026-06-15 14:31:51 +02:00
klemek d1deaad03f fix: space in words
Python Lint CI / ruff (push) Has been cancelled
Python Lint CI / ruff-format-check (push) Has been cancelled
Python Lint CI / ty (push) Has been cancelled
2026-06-15 14:31:21 +02:00
klemek 4df2bab348 chore: version 1.1.0
Python Lint CI / ruff (push) Failing after 1m35s
Python Lint CI / ruff-format-check (push) Successful in 3m3s
Python Lint CI / ty (push) Successful in 2m51s
2026-06-15 14:22:52 +02:00
klemek d5b9018ef2 feat: multiple words 2026-06-15 14:22:40 +02:00
klemek b387fec2b3 fix: check dig NS
Python Lint CI / ruff-format-check (push) Successful in 44s
Python Lint CI / ty (push) Successful in 44s
Python Lint CI / ruff (push) Successful in 44s
2026-06-15 14:18:58 +02:00
16 changed files with 2752 additions and 259 deletions
+1
View File
@@ -9,3 +9,4 @@ coverage.xml
build build
dist dist
.pytest_cache .pytest_cache
words.txt
+32 -24
View File
@@ -18,22 +18,22 @@ OPEN ?= xdg-open
.PHONY: help .PHONY: help
help: ## show this message help: ## show this message
@echo "Usage: $(MAKE) [target1] [target2] ..." echo "Usage: $(MAKE) [target1] [target2] ..."
@echo "" echo ""
@echo "Commands/Targets:" echo "Commands/Targets:"
@cat $(MAKEFILE_LIST) | grep -E '(^[a-zA-Z0-9_%-]+:.*?##.*$$)|(^##)' | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-20s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/' cat $(MAKEFILE_LIST) | grep -E '(^[a-zA-Z0-9_%-]+:.*?##.*$$)|(^##)' | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-20s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'
@echo "" echo ""
@echo "Environment:" echo "Environment:"
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z0-9_-]+\s*\??=.*$$' | grep -Eo '^[a-zA-Z0-9_-]+' | xargs -I {} $(MAKE) -s print-{} 2> /dev/null cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z0-9_-]+\s*\??=.*$$' | grep -Eo '^[a-zA-Z0-9_-]+' | xargs -I {} $(MAKE) -s print-{} 2> /dev/null
.PHONY: print-% .PHONY: print-%
print-%: print-%:
@echo -e '\033[32m$*\033[0m = $($*)' echo -e '\033[32m$*\033[0m = $($*)'
# FILES # FILES
.venv: uv.lock .venv: uv.lock
@$(MAKE) -s uv-sync $(MAKE) -s uv-sync
# ACTIONS # ACTIONS
@@ -57,7 +57,7 @@ test: unittest ## test project
.PHONY: test-% .PHONY: test-%
test-%: ## test project with specific test test-%: ## test project with specific test
@$(MAKE) -s unittest-$* $(MAKE) -s unittest-$*
.PHONY: coverage .PHONY: coverage
coverage: coverage-unittest coverage-xml coverage-report ## test project with coverage coverage: coverage-unittest coverage-xml coverage-report ## test project with coverage
@@ -66,53 +66,61 @@ coverage: coverage-unittest coverage-xml coverage-report ## test project with co
.PHONY: uv-sync .PHONY: uv-sync
uv-sync: ## uv sync uv-sync: ## uv sync
@$(UV) sync --active $(UV) sync --active
.PHONY: uv-update .PHONY: uv-update
uv-upgrade: ## uv sync upgrade uv-upgrade: ## uv sync upgrade
@$(UV) sync --active --upgrade $(UV) sync --active --upgrade
.PHONY: ruff .PHONY: ruff
ruff: .venv ## ruff check ruff: .venv ## ruff check
@$(RUFF) check $(RUFF) check
.PHONY: ruff-fix .PHONY: ruff-fix
ruff-fix: .venv ## ruff check (and fix) ruff-fix: .venv ## ruff check (and fix)
@$(RUFF) check --fix --unsafe-fixes $(RUFF) check --fix --unsafe-fixes
.PHONY: ruff-format .PHONY: ruff-format
ruff-format: .venv ## ruff format ruff-format: .venv ## ruff format
@$(RUFF) format $(RUFF) format
.PHONY: ruff-format-check .PHONY: ruff-format-check
ruff-format-check: .venv ## ruff format (check only) ruff-format-check: .venv ## ruff format (check only)
@$(RUFF) format --check $(RUFF) format --check
.PHONY: ty .PHONY: ty
ty: .venv ## ty check ty: .venv ## ty check
@$(TY) check $(TY) check
.PHONY: unittest .PHONY: unittest
unittest: .venv ## unittest unittest: .venv ## unittest
@$(UNITTEST) -v $(UNITTEST) -v
.PHONY: unittest-% .PHONY: unittest-%
unittest-%: .venv ## unittest -k [filter] unittest-%: .venv ## unittest -k [filter]
@$(UNITTEST) -v -k $* $(UNITTEST) -v -k $*
.PHONY: coverage-unittest .PHONY: coverage-unittest
coverage-unittest: .venv ## coverage run -m unittest coverage-unittest: .venv ## coverage run -m unittest
@$(COVERAGE) run -m unittest -v $(COVERAGE) run -m unittest -v
.PHONY: coverage-report .PHONY: coverage-report
coverage-report: .venv ## coverage report coverage-report: .venv ## coverage report
@$(COVERAGE) report $(COVERAGE) report
.PHONY: coverage-html .PHONY: coverage-html
coverage-html: .venv ## coverage html coverage-html: .venv ## coverage html
@$(COVERAGE) html $(COVERAGE) html
@$(OPEN) htmlcov/index.html || true $(OPEN) htmlcov/index.html || true
.PHONY: coverage-xml .PHONY: coverage-xml
coverage-xml: .venv ## coverage xml coverage-xml: .venv ## coverage xml
@$(COVERAGE) xml $(COVERAGE) xml
.PHONY: release-%
release-%: .venv ## release major/minor/patch
$(UV) version --bump=$*
git add pyproject.toml uv.lock
git commit -m "chore: $$(uv version --short)"
git tag "release/$$(uv version --short)" -m "$$(uv version)"
$(UV) build
+49 -3
View File
@@ -23,14 +23,18 @@ uvx --from git+https://git.klemek.fr/klemek/cool-domain cool-domain
## Usage ## Usage
```txt ```txt
usage: cool-domain [-h] [--count DOMAIN_COUNT] [--lang {english,french}] [--dig | --no-dig] [--debug | --no-debug] [--quiet | --no-quiet] usage: cool-domain [-h] [--count DOMAIN_COUNT] [--words WORD_COUNT] [--lang {english,french,custom}] [--custom-words CUSTOM_WORDS]
[--cache | --no-cache] [--cache-file CACHE_FILE] [--tld-list-uri TLD_LIST_URI] [--dig | --no-dig] [--debug | --no-debug] [--quiet | --no-quiet] [--cache | --no-cache] [--cache-file CACHE_FILE]
[--tld-list-uri TLD_LIST_URI]
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
--count DOMAIN_COUNT number of domains to generate (default: 1) --count DOMAIN_COUNT number of domains to generate (default: 1)
--lang {english,french} --words WORD_COUNT number of words per domain (default: 1)
--lang {english,french,custom}
word list lang (default: english) word list lang (default: english)
--custom-words CUSTOM_WORDS
custom word list location (default: words.txt)
--dig, --no-dig check availability with dig (default: true) --dig, --no-dig check availability with dig (default: true)
--debug, --no-debug --debug, --no-debug
--quiet, --no-quiet --quiet, --no-quiet
@@ -40,3 +44,45 @@ options:
--tld-list-uri TLD_LIST_URI --tld-list-uri TLD_LIST_URI
Top Level Domains list (default: https://publicsuffix.org/list/public_suffix_list.dat) Top Level Domains list (default: https://publicsuffix.org/list/public_suffix_list.dat)
``` ```
# Development
## Updating `tld.txt`
Go to [tld-list.com](https://tld-list.com) and open console.
```js
async function getValidTlds() {
const delay = (millis) =>
new Promise((resolve, reject) => {
setTimeout((_) => {
resolve();
}, millis);
});
const sourceData = await (
await fetch("https://tld-list.com/tlds-from-a-z")
).text();
const regexp = /href="\/tld\/([\w\.]+)"/gm;
const outTlds = [];
let match;
while ((match = regexp.exec(sourceData)) !== null) {
const tld = match[1];
await delay(100);
console.log(tld);
const resp = await fetch(`https://tld-list.com/tld/${tld}`);
if (resp.status === 200) {
const content = await resp.text();
if (!content.includes("Registrars (0)")) {
outTlds.push(tld);
}
}
}
return outTlds;
}
const tlds = await getValidTlds();
console.log(tlds.join(','));
```
+16 -11
View File
@@ -1,25 +1,26 @@
import logging import logging
import sys import sys
from . import PKG_NAME, PKG_VERSION from cool_domain import PKG_NAME, PKG_VERSION
from .cache import Cache from cool_domain.constants import LANGS
from .constants import LANGS from cool_domain.factory import Factory
from .factory import Factory from cool_domain.logs import setup_logs
from .logs import setup_logs from cool_domain.params import parse_parameters
from .params import parse_parameters from cool_domain.tld_list import TLDList
from .tld_list import TLDList from cool_domain.word_list import WordList
def main() -> int: def main() -> int:
params = parse_parameters(sys.argv[1:]) params = parse_parameters(sys.argv[1:])
setup_logs(params) setup_logs(params)
logging.getLogger().info("%s %s", PKG_NAME, PKG_VERSION) logging.getLogger().info("%s %s", PKG_NAME, PKG_VERSION)
langs = LANGS
langs["custom"] = WordList("custom", params.custom_words)
with ( with (
Cache(filepath=params.cache_file, no_load=params.cache) as cache, TLDList() as tld_list,
TLDList(cache=cache, remote_uri=params.tld_list_uri) as tld_list, langs[params.lang] as word_list,
LANGS[params.lang] as word_list,
): ):
factory = Factory(tld_list=tld_list, word_list=word_list) factory = Factory(tld_list=tld_list, word_list=word_list, words=params.words)
logging.getLogger().info( logging.getLogger().info(
"Searching %s domain%s", params.count, "s" if params.count > 1 else "" "Searching %s domain%s", params.count, "s" if params.count > 1 else ""
) )
@@ -29,3 +30,7 @@ def main() -> int:
else: else:
logging.getLogger().info(factory.random_domain()) logging.getLogger().info(factory.random_domain())
return 0 return 0
if __name__ == "__main__":
main()
-48
View File
@@ -1,48 +0,0 @@
import json
import pathlib
import typing
T = typing.TypeVar("T")
class Cache:
__slots__ = ["__data", "__filepath", "__no_load"]
def __init__(self, filepath: str, no_load: bool) -> None: # noqa: FBT001
self.__filepath = pathlib.Path(filepath)
self.__no_load = no_load
self.__data: dict[str, typing.Any] | None = None
def load(self) -> None:
if not self.__no_load and self.__filepath.exists():
self.__data = json.loads(self.__filepath.read_text())
else:
self.__data = {}
def write(self) -> None:
self.__filepath.write_text(json.dumps(self.__data))
def __enter__(self) -> typing.Self:
self.load()
return self
def __exit__(self, *_: object) -> None:
self.write()
def has(self, key: str) -> bool:
return self.__data is not None and key in self.__data
def get(self, key: str) -> typing.Any:
if self.__data is not None and key in self.__data:
return self.__data[key]
return None
def set(self, key: str, value: typing.Any) -> None:
if self.__data is not None:
self.__data[key] = value
self.write()
def get_or_set(self, key: str, callback: typing.Callable[[], T]) -> T:
if not self.has(key):
self.set(key, callback())
return self.get(key)
+3 -3
View File
@@ -1,6 +1,6 @@
from .word_list import WordList from cool_domain.word_list import WordList
LANGS: dict[str, WordList] = { LANGS: dict[str, WordList] = {
"english": WordList("english", "english", "words_alpha.txt"), "english": WordList("english", "english.txt"),
"french": WordList("french", "french", "francais.txt"), "french": WordList("french", "french.txt"),
} }
+1
View File
@@ -0,0 +1 @@
../english/words_alpha.txt
+12 -9
View File
@@ -4,9 +4,8 @@ import shutil
import subprocess import subprocess
import typing import typing
if typing.TYPE_CHECKING: from cool_domain.tld_list import TLDList
from .tld_list import TLDList from cool_domain.word_list import WordList
from .word_list import WordList
class FactoryError(Exception): class FactoryError(Exception):
@@ -14,12 +13,13 @@ class FactoryError(Exception):
class Factory: class Factory:
__slots__ = ["__found", "__tld_list", "__word_list", "logger", "logger"] __slots__ = ["__found", "__tld_list", "__word_list", "__words", "logger"]
def __init__(self, tld_list: TLDList, word_list: WordList) -> None: def __init__(self, tld_list: TLDList, word_list: WordList, words: int) -> None:
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__) self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
self.__tld_list = tld_list self.__tld_list = tld_list
self.__word_list = word_list self.__word_list = word_list
self.__words = words
self.__found: set[str] = set() self.__found: set[str] = set()
def __iter__(self) -> typing.Self: def __iter__(self) -> typing.Self:
@@ -41,10 +41,10 @@ class Factory:
msg = "dig is not installed" msg = "dig is not installed"
raise FactoryError(msg) raise FactoryError(msg)
try: try:
output = subprocess.check_output([dig_bin, domain, "+short"]) # noqa: S603 output = subprocess.check_output([dig_bin, domain, "NS", "+short"]) # noqa: S603
return len(output) == 0 return len(output) == 0
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
return True return False
def random_domain(self) -> str: def random_domain(self) -> str:
k = 10000 k = 10000
@@ -65,12 +65,15 @@ class Factory:
return domain return domain
def __random_domain(self) -> str | None: def __random_domain(self) -> str | None:
word = random.choice(self.__word_list.words) # noqa: S311 word = self.__random_word()
for tld in self.__tld_list_shuffled: for tld in self.__tld_list_shuffled:
tld_escaped = tld.replace(".", "") tld_escaped = tld.replace(".", "")
if word.endswith(tld_escaped) and len(word) > len(tld_escaped): if word.endswith(tld_escaped) and len(word) > len(tld_escaped):
domain = word[: -len(tld_escaped)] + "." + tld domain = word[: -len(tld_escaped)] + "." + tld
if domain not in self.__found: if "-." not in domain and domain not in self.__found:
self.__found.add(domain) self.__found.add(domain)
return domain return domain
return None return None
def __random_word(self) -> str:
return "-".join(random.sample(self.__word_list.words, self.__words))
+1
View File
@@ -0,0 +1 @@
../french/francais.txt
+1 -2
View File
@@ -2,8 +2,7 @@ import enum
import logging import logging
import typing import typing
if typing.TYPE_CHECKING: from cool_domain.params import Parameters
from .params import Parameters
class TermColor(enum.StrEnum): class TermColor(enum.StrEnum):
+20 -29
View File
@@ -1,12 +1,10 @@
import argparse import argparse
import dataclasses import dataclasses
import os import os
import pathlib
import tempfile
import typing import typing
from . import PKG_NAME from cool_domain import PKG_NAME
from .constants import LANGS from cool_domain.constants import LANGS
@dataclasses.dataclass(frozen=True, slots=True) @dataclasses.dataclass(frozen=True, slots=True)
@@ -15,13 +13,13 @@ class Parameters:
quiet: bool = False quiet: bool = False
cache: bool = True cache: bool = True
count: int = 1 count: int = 1
words: int = 1
dig: bool = True dig: bool = True
lang: str = "english" lang: str = "english"
cache_file: str = str(pathlib.Path(tempfile.gettempdir()) / ".cool-domain-cache") custom_words: str = "words.txt"
tld_list_uri: str = "https://publicsuffix.org/list/public_suffix_list.dat"
@classmethod @classmethod
def from_namespace(cls, args: argparse.Namespace) -> Parameters: def from_namespace(cls, args: argparse.Namespace) -> "Parameters":
return Parameters(**vars(args)) return Parameters(**vars(args))
@@ -89,7 +87,6 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters:
default_values = Parameters() default_values = Parameters()
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
PKG_NAME, PKG_NAME,
suggest_on_error=True,
) )
__add_arg_int( __add_arg_int(
parser, parser,
@@ -98,11 +95,25 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters:
default=default_values.count, default=default_values.count,
help_txt="number of domains to generate", help_txt="number of domains to generate",
) )
__add_arg_int(
parser,
"--words",
env_var="WORD_COUNT",
default=default_values.words,
help_txt="number of words per domain",
)
parser.add_argument( parser.add_argument(
"--lang", "--lang",
default=default_values.lang, default=default_values.lang,
help="word list lang (default: english)", help="word list lang (default: english)",
choices=LANGS.keys(), choices=[*list(LANGS.keys()), "custom"],
)
__add_arg_str(
parser,
"--custom-words",
env_var="CUSTOM_WORDS",
default=default_values.custom_words,
help_txt="custom word list location",
) )
__add_arg_bool( __add_arg_bool(
parser, parser,
@@ -116,25 +127,5 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters:
parser.add_argument( parser.add_argument(
"--quiet", action=argparse.BooleanOptionalAction, default=default_values.quiet "--quiet", action=argparse.BooleanOptionalAction, default=default_values.quiet
) )
__add_arg_bool(
parser,
"--cache",
default=default_values.cache,
help_txt="use cache",
)
__add_arg_str(
parser,
"--cache-file",
env_var="CACHE_FILE",
default=default_values.cache_file,
help_txt="cache file location",
)
__add_arg_str(
parser,
"--tld-list-uri",
env_var="TLD_LIST_URI",
default=default_values.tld_list_uri,
help_txt="Top Level Domains list",
)
parsed_args = parser.parse_args(args) parsed_args = parser.parse_args(args)
return Parameters.from_namespace(parsed_args) return Parameters.from_namespace(parsed_args)
+2598
View File
File diff suppressed because it is too large Load Diff
+9 -34
View File
@@ -1,54 +1,29 @@
import http
import logging import logging
import pathlib
import typing import typing
import requests
import unidecode
if typing.TYPE_CHECKING:
from .cache import Cache
class TLDError(Exception): class TLDError(Exception):
pass pass
class TLDList: class TLDList:
__slots__ = ["__cache", "__remote_uri", "logger", "tlds"] __slots__ = ["logger", "tlds"]
def __init__(self, cache: Cache, remote_uri: str) -> None: def __init__(self) -> None:
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__) self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
self.tlds: list[str] | None = None self.tlds: list[str] | None = None
self.__cache = cache
self.__remote_uri = remote_uri
def __load_remote(self) -> list[str]: def __load_file(self) -> list[str]:
response = requests.get(self.__remote_uri, timeout=5)
if response.status_code != http.HTTPStatus.OK:
msg = f"Failed to read {self.__remote_uri}."
raise TLDError(msg)
raw_lines = response.content.decode().split("\n")
out_lines = [] out_lines = []
for line in raw_lines: with (pathlib.Path(__file__).parent / "tld.txt").open() as file:
stripped_line = line.strip() for line in file:
if ( if len(line.strip()):
len(stripped_line) out_lines += [line.strip()]
and not stripped_line.startswith("//")
and not stripped_line.startswith("*")
and unidecode.unidecode(stripped_line) == stripped_line
):
if "." in stripped_line:
for other in out_lines:
if stripped_line.endswith(other):
break
else:
out_lines += [stripped_line]
else:
out_lines += [stripped_line]
return out_lines return out_lines
def load(self) -> None: def load(self) -> None:
self.tlds = self.__cache.get_or_set("tld_list", self.__load_remote) self.tlds = self.__load_file()
self.logger.info("Loaded %d TLD entries", len(self.tlds)) self.logger.info("Loaded %d TLD entries", len(self.tlds))
def __enter__(self) -> typing.Self: def __enter__(self) -> typing.Self:
+4 -3
View File
@@ -1,5 +1,6 @@
import logging import logging
import pathlib import pathlib
import re
import typing import typing
import unidecode import unidecode
@@ -8,10 +9,10 @@ import unidecode
class WordList: class WordList:
__slots__ = ["__filepath", "__name", "logger", "words"] __slots__ = ["__filepath", "__name", "logger", "words"]
def __init__(self, name: str, path: str, filename: str) -> None: def __init__(self, name: str, path: str) -> None:
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__) self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
self.__name = name self.__name = name
self.__filepath = pathlib.Path(__file__).parent / ".." / path / filename self.__filepath = pathlib.Path(__file__).parent / path
self.words: list[str] = [] self.words: list[str] = []
def open(self) -> None: def open(self) -> None:
@@ -22,7 +23,7 @@ class WordList:
self.logger.info("[%s] Loaded %d words", self.__name, len(self.words)) self.logger.info("[%s] Loaded %d words", self.__name, len(self.words))
def __escape_word(self, word: str) -> str: def __escape_word(self, word: str) -> str:
return unidecode.unidecode(word.strip()).lower() return re.sub(r"[^a-z0-9-]", "", unidecode.unidecode(word.strip()).lower())
def close(self) -> None: def close(self) -> None:
pass pass
+2 -3
View File
@@ -1,11 +1,10 @@
[project] [project]
name = "cool-domain" name = "cool-domain"
version = "1.0.0" version = "1.4.2"
description = "Add your description here" description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.14" requires-python = ">=3.12"
dependencies = [ dependencies = [
"requests>=2.34.2,<3.0.0",
"unidecode>=1.4.0,<2.0.0", "unidecode>=1.4.0,<2.0.0",
] ]
Generated
+3 -90
View File
@@ -1,63 +1,12 @@
version = 1 version = 1
revision = 3 revision = 3
requires-python = ">=3.14" requires-python = ">=3.12"
[[package]]
name = "certifi"
version = "2026.5.20"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" },
{ url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" },
{ url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" },
{ url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" },
{ url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" },
{ url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" },
{ url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" },
{ url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" },
{ url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" },
{ url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" },
{ url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" },
{ url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" },
{ url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" },
{ url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" },
{ url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" },
{ url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" },
{ url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" },
{ url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" },
{ url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" },
{ url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" },
{ url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" },
{ url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" },
{ url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" },
{ url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" },
{ url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" },
{ url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" },
{ url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" },
{ url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" },
{ url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" },
{ url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" },
{ url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" },
{ url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" },
{ url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
]
[[package]] [[package]]
name = "cool-domain" name = "cool-domain"
version = "1.0.0" version = "1.4.2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "requests" },
{ name = "unidecode" }, { name = "unidecode" },
] ]
@@ -68,10 +17,7 @@ dev = [
] ]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [{ name = "unidecode", specifier = ">=1.4.0,<2.0.0" }]
{ name = "requests", specifier = ">=2.34.2,<3.0.0" },
{ name = "unidecode", specifier = ">=1.4.0,<2.0.0" },
]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
@@ -79,30 +25,6 @@ dev = [
{ name = "ty", specifier = ">=0.0.34,<0.0.35" }, { name = "ty", specifier = ">=0.0.34,<0.0.35" },
] ]
[[package]]
name = "idna"
version = "3.18"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
]
[[package]]
name = "requests"
version = "2.34.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
]
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.15.12" version = "0.15.12"
@@ -160,12 +82,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/94/7d/a8a765761bbc0c836
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/b7/559f59d57d18b44c6d1250d2eeaa676e028b9c527431f5d0736478a73ba1/Unidecode-1.4.0-py3-none-any.whl", hash = "sha256:c3c7606c27503ad8d501270406e345ddb480a7b5f38827eafe4fa82a137f0021", size = 235837, upload-time = "2025-04-24T08:45:01.609Z" }, { url = "https://files.pythonhosted.org/packages/8f/b7/559f59d57d18b44c6d1250d2eeaa676e028b9c527431f5d0736478a73ba1/Unidecode-1.4.0-py3-none-any.whl", hash = "sha256:c3c7606c27503ad8d501270406e345ddb480a7b5f38827eafe4fa82a137f0021", size = 235837, upload-time = "2025-04-24T08:45:01.609Z" },
] ]
[[package]]
name = "urllib3"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
]