nord-config-generator 1.0.3__tar.gz → 1.0.4__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.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: nord-config-generator
3
+ Version: 1.0.4
4
+ Summary: A command-line tool for generating optimized NordVPN WireGuard configurations.
5
+ Author-email: Ahmed Touhami <mustafachyi272@gmail.com>
6
+ Project-URL: Homepage, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator
7
+ Project-URL: Bug Tracker, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Topic :: System :: Networking
12
+ Classifier: Environment :: Console
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: aiohttp<4.0,>=3.12.14
16
+ Requires-Dist: aiofiles<25.0,>=24.1.0
17
+ Requires-Dist: rich<15.0,>=14.0.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nord-config-generator"
7
- version = "1.0.3"
7
+ version = "1.0.4"
8
8
  authors = [
9
9
  { name="Ahmed Touhami", email="mustafachyi272@gmail.com" },
10
10
  ]
@@ -3,7 +3,6 @@ import os
3
3
  import asyncio
4
4
  import json
5
5
  import base64
6
- import re
7
6
  import time
8
7
  from typing import List, Tuple, Optional, Dict, Any
9
8
  from dataclasses import dataclass
@@ -70,7 +69,7 @@ class NordVpnApiClient:
70
69
 
71
70
  async def get_all_servers(self) -> List[Dict[str, Any]]:
72
71
  url = f"{self.NORD_API_BASE_URL}/servers"
73
- params = {'limit': 9000, 'filters[servers_technologies][identifier]': 'wireguard_udp'}
72
+ params = {'limit': 16384, 'filters[servers_technologies][identifier]': 'wireguard_udp'}
74
73
  data = await self._get(url, params=params)
75
74
  return data if isinstance(data, list) else []
76
75
 
@@ -98,6 +97,7 @@ class NordVpnApiClient:
98
97
 
99
98
  class ConfigurationOrchestrator:
100
99
  CONCURRENT_LIMIT = 200
100
+ _path_sanitizer = str.maketrans('', '', '<>:"/\\|?*\0')
101
101
 
102
102
  def __init__(self, private_key: str, preferences: UserPreferences, console_manager: ConsoleManager, api_client: NordVpnApiClient):
103
103
  self._private_key = private_key
@@ -115,6 +115,12 @@ class ConfigurationOrchestrator:
115
115
 
116
116
  processed_servers = await self._process_server_data(all_servers_data, user_location)
117
117
 
118
+ unique_servers = {}
119
+ for s in processed_servers:
120
+ if s.name not in unique_servers:
121
+ unique_servers[s.name] = s
122
+ processed_servers = list(unique_servers.values())
123
+
118
124
  sorted_servers = sorted(processed_servers, key=lambda s: (s.load, s.distance))
119
125
  best_servers_by_location = self._get_best_servers(sorted_servers)
120
126
 
@@ -159,6 +165,8 @@ class ConfigurationOrchestrator:
159
165
  return info
160
166
 
161
167
  async def _save_all_configurations(self, sorted_servers: List[Server], best_servers: Dict, servers_info: Dict):
168
+ used_paths: Dict[str, int] = {}
169
+
162
170
  with self._console.create_progress_bar(transient=False) as progress:
163
171
  self.stats.total_configs = len(sorted_servers)
164
172
  self.stats.best_configs = len(best_servers)
@@ -166,18 +174,46 @@ class ConfigurationOrchestrator:
166
174
  task_all = progress.add_task("Generating standard configs...", total=self.stats.total_configs)
167
175
  task_best = progress.add_task("Generating optimized configs...", total=self.stats.best_configs)
168
176
 
169
- save_tasks = [self._create_save_task(s, 'configs', progress, task_all) for s in sorted_servers]
170
- save_tasks.extend([self._create_save_task(s, 'best_configs', progress, task_best) for s in best_servers.values()])
177
+ save_tasks = []
178
+ save_tasks.extend(self._create_batch_save_tasks(sorted_servers, 'configs', progress, task_all, used_paths))
179
+ save_tasks.extend(self._create_batch_save_tasks(list(best_servers.values()), 'best_configs', progress, task_best, used_paths))
171
180
 
172
181
  await asyncio.gather(*save_tasks)
173
182
  async with aiofiles.open(self._output_dir / 'servers.json', 'w') as f:
174
183
  await f.write(json.dumps(servers_info, indent=2, separators=(',', ':'), ensure_ascii=False))
175
184
 
176
- def _create_save_task(self, server: Server, subfolder: str, progress, task_id):
177
- config_str = self._generate_wireguard_config_string(server, self._preferences, self._private_key)
178
- path = self._output_dir / subfolder / self._sanitize_path_part(server.country) / self._sanitize_path_part(server.city)
179
- filename = self._generate_compliant_filename(server)
180
- return self._save_config_file(config_str, path, filename, progress, task_id)
185
+ def _create_batch_save_tasks(self, servers: List[Server], subfolder: str, progress, task_id, used_paths: Dict[str, int]):
186
+ tasks = []
187
+ for server in servers:
188
+ country_clean = self._sanitize_path_part(server.country)
189
+ city_clean = self._sanitize_path_part(server.city)
190
+ dir_path = self._output_dir / subfolder / country_clean / city_clean
191
+
192
+ base_filename = self._extract_base_filename(server)
193
+ rel_path = f"{subfolder}/{country_clean}/{city_clean}/{base_filename}"
194
+
195
+ if rel_path in used_paths:
196
+ idx = used_paths[rel_path]
197
+ if idx == 0: idx = 1
198
+
199
+ base_path_no_ext = rel_path[:-5]
200
+ base_name_no_ext = base_filename[:-5]
201
+
202
+ while True:
203
+ new_rel = f"{base_path_no_ext}_{idx}.conf"
204
+ if new_rel not in used_paths:
205
+ used_paths[rel_path] = idx + 1
206
+ used_paths[new_rel] = 0
207
+ filename = f"{base_name_no_ext}_{idx}.conf"
208
+ break
209
+ idx += 1
210
+ else:
211
+ filename = base_filename
212
+ used_paths[rel_path] = 0
213
+
214
+ config_str = self._generate_wireguard_config_string(server, self._preferences, self._private_key)
215
+ tasks.append(self._save_config_file(config_str, dir_path, filename, progress, task_id))
216
+ return tasks
181
217
 
182
218
  async def _save_config_file(self, config_string: str, path: Path, filename: str, progress, task_id):
183
219
  path.mkdir(parents=True, exist_ok=True)
@@ -187,15 +223,23 @@ class ConfigurationOrchestrator:
187
223
  progress.update(task_id, advance=1)
188
224
 
189
225
  @staticmethod
190
- def _generate_compliant_filename(server: Server) -> str:
191
- server_number_match = re.search(r'\d+$', server.name)
192
- if not server_number_match:
193
- fallback_name = f"wg{server.station.replace('.', '')}"
194
- return f"{fallback_name[:15]}.conf"
226
+ def _extract_base_filename(server: Server) -> str:
227
+ s = server.name
228
+ num = ""
229
+ for i in range(len(s) - 1, -1, -1):
230
+ if s[i].isdigit():
231
+ start = i
232
+ while start >= 0 and s[start].isdigit():
233
+ start -= 1
234
+ num = s[start+1 : i+1]
235
+ break
195
236
 
196
- server_number = server_number_match.group(0)
197
- base_name = f"{server.country_code}{server_number}"
198
- return f"{base_name[:15]}.conf"
237
+ if not num:
238
+ fallback = f"wg{server.station.replace('.', '')}"
239
+ return f"{fallback[:15]}.conf"
240
+
241
+ base = f"{server.country_code}{num}"
242
+ return f"{base[:15]}.conf"
199
243
 
200
244
  @staticmethod
201
245
  def _generate_wireguard_config_string(server: Server, preferences: UserPreferences, private_key: str) -> str:
@@ -236,9 +280,9 @@ class ConfigurationOrchestrator:
236
280
  c = 2 * asin(sqrt(a))
237
281
  return c * 6371
238
282
 
239
- @staticmethod
240
- def _sanitize_path_part(part: str) -> str:
241
- return re.sub(r'[<>:"/\\|?*\0]', '', part.lower().replace(' ', '_')).replace('#', '')
283
+ @classmethod
284
+ def _sanitize_path_part(cls, part: str) -> str:
285
+ return part.lower().replace(' ', '_').replace('#', '').translate(cls._path_sanitizer)
242
286
 
243
287
  class Application:
244
288
  def __init__(self):
@@ -291,22 +335,29 @@ class Application:
291
335
  user_input = self._console.get_preferences(defaults)
292
336
 
293
337
  dns_input = user_input.get("dns")
294
- dns = dns_input if dns_input and re.match(r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$', dns_input) else defaults.dns
295
-
338
+ if dns_input:
339
+ parts = dns_input.split('.')
340
+ if len(parts) == 4 and all(p.isdigit() and 0 <= int(p) <= 255 for p in parts):
341
+ defaults.dns = dns_input
342
+
296
343
  use_ip = user_input.get("endpoint_type", "").lower() == 'y'
297
344
 
298
- keepalive = defaults.persistent_keepalive
299
345
  keepalive_input = user_input.get("keepalive")
300
346
  if keepalive_input and keepalive_input.isdigit():
301
347
  keepalive_val = int(keepalive_input)
302
348
  if 15 <= keepalive_val <= 120:
303
- keepalive = keepalive_val
349
+ defaults.persistent_keepalive = keepalive_val
304
350
 
305
- return UserPreferences(dns=dns, use_ip_for_endpoint=use_ip, persistent_keepalive=keepalive)
351
+ return UserPreferences(
352
+ dns=defaults.dns,
353
+ use_ip_for_endpoint=use_ip,
354
+ persistent_keepalive=defaults.persistent_keepalive
355
+ )
306
356
 
307
357
  async def _get_validated_private_key(self, api_client: NordVpnApiClient) -> Optional[str]:
308
358
  token = self._console.get_user_input("Please enter your NordVPN access token: ", is_secret=True)
309
- if not re.match(r'^[a-fA-F0-9]{64}$', token):
359
+ is_hex = len(token) == 64 and all(c in '0123456789abcdefABCDEF' for c in token)
360
+ if not is_hex:
310
361
  self._console.print_message("error", "Invalid token format.")
311
362
  return None
312
363
 
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: nord-config-generator
3
+ Version: 1.0.4
4
+ Summary: A command-line tool for generating optimized NordVPN WireGuard configurations.
5
+ Author-email: Ahmed Touhami <mustafachyi272@gmail.com>
6
+ Project-URL: Homepage, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator
7
+ Project-URL: Bug Tracker, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Topic :: System :: Networking
12
+ Classifier: Environment :: Console
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: aiohttp<4.0,>=3.12.14
16
+ Requires-Dist: aiofiles<25.0,>=24.1.0
17
+ Requires-Dist: rich<15.0,>=14.0.0
@@ -1,4 +1,3 @@
1
- README.md
2
1
  pyproject.toml
3
2
  src/nord_config_generator/__init__.py
4
3
  src/nord_config_generator/main.py
@@ -1,90 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: nord-config-generator
3
- Version: 1.0.3
4
- Summary: A command-line tool for generating optimized NordVPN WireGuard configurations.
5
- Author-email: Ahmed Touhami <mustafachyi272@gmail.com>
6
- Project-URL: Homepage, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator
7
- Project-URL: Bug Tracker, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator/issues
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Topic :: System :: Networking
12
- Classifier: Environment :: Console
13
- Requires-Python: >=3.9
14
- Description-Content-Type: text/markdown
15
- Requires-Dist: aiohttp<4.0,>=3.12.14
16
- Requires-Dist: aiofiles<25.0,>=24.1.0
17
- Requires-Dist: rich<15.0,>=14.0.0
18
-
19
- # NordVPN WireGuard Configuration Generator
20
-
21
- A command-line tool for generating optimized NordVPN WireGuard configurations.
22
-
23
- ## Project Philosophy: A Focus on Quality
24
-
25
- This project has been fundamentally refocused. Previously, multiple versions existed across several programming languages. This approach divided development effort and resulted in inconsistent quality.
26
-
27
- The new directive is singular: to provide one exceptionally engineered tool that is robust, maintainable, and correct.
28
-
29
- To this end, all previous language implementations have been archived. Development is now concentrated on two platforms:
30
-
31
- 1. **This Command-Line Tool:** A complete rewrite in Python, packaged for professional use.
32
- 2. **A Web Interface:** For users who require a graphical frontend.
33
-
34
- This consolidated effort ensures a higher standard of quality and a more reliable end-product.
35
-
36
- ## Core Capabilities
37
-
38
- * **Package Distribution:** The tool is a proper command-line application, installable via PyPI. This eliminates manual dependency management.
39
- * **Performance:** Asynchronous architecture processes the entire NordVPN server list in seconds.
40
- * **Optimization:** Intelligently sorts servers by current load and geographic proximity to the user, generating configurations for the most performant connections.
41
- * **Structured Output:** Automatically creates a clean directory structure containing standard configurations, a `best_configs` folder for optimal servers per location, and a `servers.json` file with detailed metadata for analysis.
42
- * **Interactive and Non-Interactive:** A guided rich-CLI for interactive use. The core logic is structured to be scriptable.
43
-
44
- ## Installation
45
-
46
- Prerequisites: Python 3.9+
47
-
48
- Install the package using `pip`:
49
-
50
- ```bash
51
- pip install nord-config-generator
52
- ```
53
-
54
- ## Usage
55
-
56
- ### Generate Configurations (Default Action)
57
-
58
- Execute the application without any arguments. This is the primary function.
59
-
60
- ```bash
61
- nordgen
62
- ```
63
-
64
- The application will prompt for the required access token and configuration preferences.
65
-
66
- ### Retrieve Private Key
67
-
68
- To retrieve and display your NordLynx private key without generating configurations, use the `get-key` command:
69
-
70
- ```bash
71
- nordgen get-key
72
- ```
73
-
74
- ## Web Version
75
-
76
- A graphical alternative is available for direct use in a web browser.
77
-
78
- * **Current Version:** [https://nord-configs.selfhoster.nl/](https://nord-configs.selfhoster.nl/)
79
- * **Legacy Version:** [https://wg-nord.pages.dev/](https://wg-nord.pages.dev/)
80
-
81
- ## Support
82
-
83
- Project visibility and continued development are supported by two actions:
84
-
85
- 1. **Star the Repository:** Starring the project on GitHub increases its visibility.
86
- 2. **NordVPN Referral:** Using the referral link for new subscriptions provides support at no additional cost. Link: [https://ref.nordvpn.com/MXIVDoJGpKT](https://ref.nordvpn.com/MXIVDoJGpKT)
87
-
88
- ## License
89
-
90
- This project is distributed under the GNU General Public License v3.0. See the `LICENSE` file for full details.
@@ -1,72 +0,0 @@
1
- # NordVPN WireGuard Configuration Generator
2
-
3
- A command-line tool for generating optimized NordVPN WireGuard configurations.
4
-
5
- ## Project Philosophy: A Focus on Quality
6
-
7
- This project has been fundamentally refocused. Previously, multiple versions existed across several programming languages. This approach divided development effort and resulted in inconsistent quality.
8
-
9
- The new directive is singular: to provide one exceptionally engineered tool that is robust, maintainable, and correct.
10
-
11
- To this end, all previous language implementations have been archived. Development is now concentrated on two platforms:
12
-
13
- 1. **This Command-Line Tool:** A complete rewrite in Python, packaged for professional use.
14
- 2. **A Web Interface:** For users who require a graphical frontend.
15
-
16
- This consolidated effort ensures a higher standard of quality and a more reliable end-product.
17
-
18
- ## Core Capabilities
19
-
20
- * **Package Distribution:** The tool is a proper command-line application, installable via PyPI. This eliminates manual dependency management.
21
- * **Performance:** Asynchronous architecture processes the entire NordVPN server list in seconds.
22
- * **Optimization:** Intelligently sorts servers by current load and geographic proximity to the user, generating configurations for the most performant connections.
23
- * **Structured Output:** Automatically creates a clean directory structure containing standard configurations, a `best_configs` folder for optimal servers per location, and a `servers.json` file with detailed metadata for analysis.
24
- * **Interactive and Non-Interactive:** A guided rich-CLI for interactive use. The core logic is structured to be scriptable.
25
-
26
- ## Installation
27
-
28
- Prerequisites: Python 3.9+
29
-
30
- Install the package using `pip`:
31
-
32
- ```bash
33
- pip install nord-config-generator
34
- ```
35
-
36
- ## Usage
37
-
38
- ### Generate Configurations (Default Action)
39
-
40
- Execute the application without any arguments. This is the primary function.
41
-
42
- ```bash
43
- nordgen
44
- ```
45
-
46
- The application will prompt for the required access token and configuration preferences.
47
-
48
- ### Retrieve Private Key
49
-
50
- To retrieve and display your NordLynx private key without generating configurations, use the `get-key` command:
51
-
52
- ```bash
53
- nordgen get-key
54
- ```
55
-
56
- ## Web Version
57
-
58
- A graphical alternative is available for direct use in a web browser.
59
-
60
- * **Current Version:** [https://nord-configs.selfhoster.nl/](https://nord-configs.selfhoster.nl/)
61
- * **Legacy Version:** [https://wg-nord.pages.dev/](https://wg-nord.pages.dev/)
62
-
63
- ## Support
64
-
65
- Project visibility and continued development are supported by two actions:
66
-
67
- 1. **Star the Repository:** Starring the project on GitHub increases its visibility.
68
- 2. **NordVPN Referral:** Using the referral link for new subscriptions provides support at no additional cost. Link: [https://ref.nordvpn.com/MXIVDoJGpKT](https://ref.nordvpn.com/MXIVDoJGpKT)
69
-
70
- ## License
71
-
72
- This project is distributed under the GNU General Public License v3.0. See the `LICENSE` file for full details.
@@ -1,90 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: nord-config-generator
3
- Version: 1.0.3
4
- Summary: A command-line tool for generating optimized NordVPN WireGuard configurations.
5
- Author-email: Ahmed Touhami <mustafachyi272@gmail.com>
6
- Project-URL: Homepage, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator
7
- Project-URL: Bug Tracker, https://github.com/mustafachyi/NordVPN-WireGuard-Config-Generator/issues
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Topic :: System :: Networking
12
- Classifier: Environment :: Console
13
- Requires-Python: >=3.9
14
- Description-Content-Type: text/markdown
15
- Requires-Dist: aiohttp<4.0,>=3.12.14
16
- Requires-Dist: aiofiles<25.0,>=24.1.0
17
- Requires-Dist: rich<15.0,>=14.0.0
18
-
19
- # NordVPN WireGuard Configuration Generator
20
-
21
- A command-line tool for generating optimized NordVPN WireGuard configurations.
22
-
23
- ## Project Philosophy: A Focus on Quality
24
-
25
- This project has been fundamentally refocused. Previously, multiple versions existed across several programming languages. This approach divided development effort and resulted in inconsistent quality.
26
-
27
- The new directive is singular: to provide one exceptionally engineered tool that is robust, maintainable, and correct.
28
-
29
- To this end, all previous language implementations have been archived. Development is now concentrated on two platforms:
30
-
31
- 1. **This Command-Line Tool:** A complete rewrite in Python, packaged for professional use.
32
- 2. **A Web Interface:** For users who require a graphical frontend.
33
-
34
- This consolidated effort ensures a higher standard of quality and a more reliable end-product.
35
-
36
- ## Core Capabilities
37
-
38
- * **Package Distribution:** The tool is a proper command-line application, installable via PyPI. This eliminates manual dependency management.
39
- * **Performance:** Asynchronous architecture processes the entire NordVPN server list in seconds.
40
- * **Optimization:** Intelligently sorts servers by current load and geographic proximity to the user, generating configurations for the most performant connections.
41
- * **Structured Output:** Automatically creates a clean directory structure containing standard configurations, a `best_configs` folder for optimal servers per location, and a `servers.json` file with detailed metadata for analysis.
42
- * **Interactive and Non-Interactive:** A guided rich-CLI for interactive use. The core logic is structured to be scriptable.
43
-
44
- ## Installation
45
-
46
- Prerequisites: Python 3.9+
47
-
48
- Install the package using `pip`:
49
-
50
- ```bash
51
- pip install nord-config-generator
52
- ```
53
-
54
- ## Usage
55
-
56
- ### Generate Configurations (Default Action)
57
-
58
- Execute the application without any arguments. This is the primary function.
59
-
60
- ```bash
61
- nordgen
62
- ```
63
-
64
- The application will prompt for the required access token and configuration preferences.
65
-
66
- ### Retrieve Private Key
67
-
68
- To retrieve and display your NordLynx private key without generating configurations, use the `get-key` command:
69
-
70
- ```bash
71
- nordgen get-key
72
- ```
73
-
74
- ## Web Version
75
-
76
- A graphical alternative is available for direct use in a web browser.
77
-
78
- * **Current Version:** [https://nord-configs.selfhoster.nl/](https://nord-configs.selfhoster.nl/)
79
- * **Legacy Version:** [https://wg-nord.pages.dev/](https://wg-nord.pages.dev/)
80
-
81
- ## Support
82
-
83
- Project visibility and continued development are supported by two actions:
84
-
85
- 1. **Star the Repository:** Starring the project on GitHub increases its visibility.
86
- 2. **NordVPN Referral:** Using the referral link for new subscriptions provides support at no additional cost. Link: [https://ref.nordvpn.com/MXIVDoJGpKT](https://ref.nordvpn.com/MXIVDoJGpKT)
87
-
88
- ## License
89
-
90
- This project is distributed under the GNU General Public License v3.0. See the `LICENSE` file for full details.