nord-config-generator 1.0.1__tar.gz → 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/PKG-INFO +1 -1
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/pyproject.toml +1 -1
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator/main.py +40 -14
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator.egg-info/PKG-INFO +1 -1
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/README.md +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/setup.cfg +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator/__init__.py +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator/ui.py +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator.egg-info/SOURCES.txt +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator.egg-info/dependency_links.txt +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator.egg-info/entry_points.txt +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator.egg-info/requires.txt +0 -0
- {nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nord-config-generator
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: A command-line tool for generating optimized NordVPN WireGuard configurations.
|
|
5
5
|
Author-email: Ahmed Touhami <mustafachyi272@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator
|
{nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator/main.py
RENAMED
|
@@ -25,6 +25,7 @@ class Server:
|
|
|
25
25
|
station: str
|
|
26
26
|
load: int
|
|
27
27
|
country: str
|
|
28
|
+
country_code: str
|
|
28
29
|
city: str
|
|
29
30
|
latitude: float
|
|
30
31
|
longitude: float
|
|
@@ -45,6 +46,7 @@ class GenerationStats:
|
|
|
45
46
|
class NordVpnApiClient:
|
|
46
47
|
NORD_API_BASE_URL = "https://api.nordvpn.com/v1"
|
|
47
48
|
LOCATION_API_URL = "https://ipinfo.io/json"
|
|
49
|
+
COUNTRIES_API_URL = f"{NORD_API_BASE_URL}/servers/countries"
|
|
48
50
|
|
|
49
51
|
def __init__(self, console_manager: ConsoleManager):
|
|
50
52
|
self._console = console_manager
|
|
@@ -73,6 +75,10 @@ class NordVpnApiClient:
|
|
|
73
75
|
data = await self._get(url, params=params)
|
|
74
76
|
return data if isinstance(data, list) else []
|
|
75
77
|
|
|
78
|
+
async def get_countries(self) -> List[Dict[str, Any]]:
|
|
79
|
+
data = await self._get(self.COUNTRIES_API_URL)
|
|
80
|
+
return data if isinstance(data, list) else []
|
|
81
|
+
|
|
76
82
|
async def get_user_geolocation(self) -> Optional[Tuple[float, float]]:
|
|
77
83
|
data = await self._get(self.LOCATION_API_URL)
|
|
78
84
|
if not isinstance(data, dict):
|
|
@@ -108,11 +114,13 @@ class ConfigurationOrchestrator:
|
|
|
108
114
|
self.stats = GenerationStats()
|
|
109
115
|
|
|
110
116
|
async def generate(self) -> Optional[Path]:
|
|
111
|
-
user_location, all_servers_data = await self._fetch_remote_data()
|
|
112
|
-
if not user_location or not all_servers_data:
|
|
117
|
+
user_location, all_servers_data, countries_data = await self._fetch_remote_data()
|
|
118
|
+
if not user_location or not all_servers_data or not countries_data:
|
|
113
119
|
return None
|
|
114
120
|
|
|
115
|
-
|
|
121
|
+
country_code_map = {country['name']: country['code'].lower() for country in countries_data}
|
|
122
|
+
processed_servers = await self._process_server_data(all_servers_data, user_location, country_code_map)
|
|
123
|
+
|
|
116
124
|
sorted_servers = sorted(processed_servers, key=lambda s: (s.load, s.distance))
|
|
117
125
|
best_servers_by_location = self._get_best_servers(sorted_servers)
|
|
118
126
|
|
|
@@ -122,19 +130,20 @@ class ConfigurationOrchestrator:
|
|
|
122
130
|
await self._save_all_configurations(sorted_servers, best_servers_by_location, servers_info)
|
|
123
131
|
return self._output_dir
|
|
124
132
|
|
|
125
|
-
async def _fetch_remote_data(self) -> Tuple[Optional[Tuple[float, float]], List[Dict[str, Any]]]:
|
|
133
|
+
async def _fetch_remote_data(self) -> Tuple[Optional[Tuple[float, float]], List[Dict[str, Any]], List[Dict[str, Any]]]:
|
|
126
134
|
with self._console.create_progress_bar() as progress:
|
|
127
|
-
task = progress.add_task("Fetching remote data...", total=
|
|
128
|
-
user_location, all_servers_data = await asyncio.gather(
|
|
135
|
+
task = progress.add_task("Fetching remote data...", total=3)
|
|
136
|
+
user_location, all_servers_data, countries_data = await asyncio.gather(
|
|
129
137
|
self._api_client.get_user_geolocation(),
|
|
130
|
-
self._api_client.get_all_servers()
|
|
138
|
+
self._api_client.get_all_servers(),
|
|
139
|
+
self._api_client.get_countries()
|
|
131
140
|
)
|
|
132
|
-
progress.update(task, advance=
|
|
133
|
-
return user_location, all_servers_data
|
|
141
|
+
progress.update(task, advance=3)
|
|
142
|
+
return user_location, all_servers_data, countries_data
|
|
134
143
|
|
|
135
|
-
async def _process_server_data(self, all_servers_data: List[Dict[str, Any]], user_location: Tuple[float, float]) -> List[Server]:
|
|
144
|
+
async def _process_server_data(self, all_servers_data: List[Dict[str, Any]], user_location: Tuple[float, float], country_code_map: Dict[str, str]) -> List[Server]:
|
|
136
145
|
loop = asyncio.get_running_loop()
|
|
137
|
-
parse_func = partial(self._parse_server_data, user_location=user_location)
|
|
146
|
+
parse_func = partial(self._parse_server_data, user_location=user_location, country_code_map=country_code_map)
|
|
138
147
|
with ThreadPoolExecutor(max_workers=min(32, (os.cpu_count() or 1) + 4)) as executor:
|
|
139
148
|
tasks = [loop.run_in_executor(executor, parse_func, s) for s in all_servers_data]
|
|
140
149
|
processed_servers = await asyncio.gather(*tasks)
|
|
@@ -174,7 +183,7 @@ class ConfigurationOrchestrator:
|
|
|
174
183
|
def _create_save_task(self, server: Server, subfolder: str, progress, task_id):
|
|
175
184
|
config_str = self._generate_wireguard_config_string(server, self._preferences, self._private_key)
|
|
176
185
|
path = self._output_dir / subfolder / self._sanitize_path_part(server.country) / self._sanitize_path_part(server.city)
|
|
177
|
-
filename =
|
|
186
|
+
filename = self._generate_compliant_filename(server)
|
|
178
187
|
return self._save_config_file(config_str, path, filename, progress, task_id)
|
|
179
188
|
|
|
180
189
|
async def _save_config_file(self, config_string: str, path: Path, filename: str, progress, task_id):
|
|
@@ -184,15 +193,31 @@ class ConfigurationOrchestrator:
|
|
|
184
193
|
await f.write(config_string)
|
|
185
194
|
progress.update(task_id, advance=1)
|
|
186
195
|
|
|
196
|
+
@staticmethod
|
|
197
|
+
def _generate_compliant_filename(server: Server) -> str:
|
|
198
|
+
server_number_match = re.search(r'\d+$', server.name)
|
|
199
|
+
if not server_number_match:
|
|
200
|
+
fallback_name = f"wg{server.station.replace('.', '')}"
|
|
201
|
+
return f"{fallback_name[:15]}.conf"
|
|
202
|
+
|
|
203
|
+
server_number = server_number_match.group(0)
|
|
204
|
+
base_name = f"{server.country_code}{server_number}"
|
|
205
|
+
return f"{base_name[:15]}.conf"
|
|
206
|
+
|
|
187
207
|
@staticmethod
|
|
188
208
|
def _generate_wireguard_config_string(server: Server, preferences: UserPreferences, private_key: str) -> str:
|
|
189
209
|
endpoint = server.station if preferences.use_ip_for_endpoint else server.hostname
|
|
190
210
|
return f"[Interface]\nPrivateKey = {private_key}\nAddress = 10.5.0.2/16\nDNS = {preferences.dns}\n\n[Peer]\nPublicKey = {server.public_key}\nAllowedIPs = 0.0.0.0/0, ::/0\nEndpoint = {endpoint}:51820\nPersistentKeepalive = {preferences.persistent_keepalive}"
|
|
191
211
|
|
|
192
212
|
@staticmethod
|
|
193
|
-
def _parse_server_data(server_data: Dict[str, Any], user_location: Tuple[float, float]) -> Optional[Server]:
|
|
213
|
+
def _parse_server_data(server_data: Dict[str, Any], user_location: Tuple[float, float], country_code_map: Dict[str, str]) -> Optional[Server]:
|
|
194
214
|
try:
|
|
195
215
|
location = server_data['locations'][0]
|
|
216
|
+
country_name = location['country']['name']
|
|
217
|
+
country_code = country_code_map.get(country_name)
|
|
218
|
+
if not country_code:
|
|
219
|
+
return None
|
|
220
|
+
|
|
196
221
|
public_key = next(
|
|
197
222
|
m['value'] for t in server_data['technologies']
|
|
198
223
|
if t['identifier'] == 'wireguard_udp'
|
|
@@ -204,7 +229,8 @@ class ConfigurationOrchestrator:
|
|
|
204
229
|
return Server(
|
|
205
230
|
name=server_data['name'], hostname=server_data['hostname'],
|
|
206
231
|
station=server_data['station'], load=int(server_data.get('load', 0)),
|
|
207
|
-
country=
|
|
232
|
+
country=country_name, country_code=country_code,
|
|
233
|
+
city=location['country'].get('city', {}).get('name', 'Unknown'),
|
|
208
234
|
latitude=location['latitude'], longitude=location['longitude'],
|
|
209
235
|
public_key=public_key, distance=distance
|
|
210
236
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nord-config-generator
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: A command-line tool for generating optimized NordVPN WireGuard configurations.
|
|
5
5
|
Author-email: Ahmed Touhami <mustafachyi272@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator
|
|
File without changes
|
|
File without changes
|
{nord_config_generator-1.0.1 → nord_config_generator-1.0.2}/src/nord_config_generator/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|