nord-config-generator 1.0.2__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.
- nord_config_generator-1.0.4/PKG-INFO +17 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/pyproject.toml +1 -1
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator/main.py +93 -52
- nord_config_generator-1.0.4/src/nord_config_generator.egg-info/PKG-INFO +17 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator.egg-info/SOURCES.txt +0 -1
- nord_config_generator-1.0.2/PKG-INFO +0 -90
- nord_config_generator-1.0.2/README.md +0 -72
- nord_config_generator-1.0.2/src/nord_config_generator.egg-info/PKG-INFO +0 -90
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/setup.cfg +0 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator/__init__.py +0 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator/ui.py +0 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator.egg-info/dependency_links.txt +0 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator.egg-info/entry_points.txt +0 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator.egg-info/requires.txt +0 -0
- {nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator.egg-info/top_level.txt +0 -0
|
@@ -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
|
{nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/src/nord_config_generator/main.py
RENAMED
|
@@ -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
|
|
@@ -46,7 +45,6 @@ class GenerationStats:
|
|
|
46
45
|
class NordVpnApiClient:
|
|
47
46
|
NORD_API_BASE_URL = "https://api.nordvpn.com/v1"
|
|
48
47
|
LOCATION_API_URL = "https://ipinfo.io/json"
|
|
49
|
-
COUNTRIES_API_URL = f"{NORD_API_BASE_URL}/servers/countries"
|
|
50
48
|
|
|
51
49
|
def __init__(self, console_manager: ConsoleManager):
|
|
52
50
|
self._console = console_manager
|
|
@@ -71,14 +69,10 @@ class NordVpnApiClient:
|
|
|
71
69
|
|
|
72
70
|
async def get_all_servers(self) -> List[Dict[str, Any]]:
|
|
73
71
|
url = f"{self.NORD_API_BASE_URL}/servers"
|
|
74
|
-
params = {'limit':
|
|
72
|
+
params = {'limit': 16384, 'filters[servers_technologies][identifier]': 'wireguard_udp'}
|
|
75
73
|
data = await self._get(url, params=params)
|
|
76
74
|
return data if isinstance(data, list) else []
|
|
77
75
|
|
|
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
|
-
|
|
82
76
|
async def get_user_geolocation(self) -> Optional[Tuple[float, float]]:
|
|
83
77
|
data = await self._get(self.LOCATION_API_URL)
|
|
84
78
|
if not isinstance(data, dict):
|
|
@@ -103,6 +97,7 @@ class NordVpnApiClient:
|
|
|
103
97
|
|
|
104
98
|
class ConfigurationOrchestrator:
|
|
105
99
|
CONCURRENT_LIMIT = 200
|
|
100
|
+
_path_sanitizer = str.maketrans('', '', '<>:"/\\|?*\0')
|
|
106
101
|
|
|
107
102
|
def __init__(self, private_key: str, preferences: UserPreferences, console_manager: ConsoleManager, api_client: NordVpnApiClient):
|
|
108
103
|
self._private_key = private_key
|
|
@@ -114,12 +109,17 @@ class ConfigurationOrchestrator:
|
|
|
114
109
|
self.stats = GenerationStats()
|
|
115
110
|
|
|
116
111
|
async def generate(self) -> Optional[Path]:
|
|
117
|
-
user_location, all_servers_data
|
|
118
|
-
if not user_location or not all_servers_data
|
|
112
|
+
user_location, all_servers_data = await self._fetch_remote_data()
|
|
113
|
+
if not user_location or not all_servers_data:
|
|
119
114
|
return None
|
|
120
115
|
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
processed_servers = await self._process_server_data(all_servers_data, user_location)
|
|
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
123
|
|
|
124
124
|
sorted_servers = sorted(processed_servers, key=lambda s: (s.load, s.distance))
|
|
125
125
|
best_servers_by_location = self._get_best_servers(sorted_servers)
|
|
@@ -130,20 +130,19 @@ class ConfigurationOrchestrator:
|
|
|
130
130
|
await self._save_all_configurations(sorted_servers, best_servers_by_location, servers_info)
|
|
131
131
|
return self._output_dir
|
|
132
132
|
|
|
133
|
-
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]]]:
|
|
134
134
|
with self._console.create_progress_bar() as progress:
|
|
135
|
-
task = progress.add_task("Fetching remote data...", total=
|
|
136
|
-
user_location, all_servers_data
|
|
135
|
+
task = progress.add_task("Fetching remote data...", total=2)
|
|
136
|
+
user_location, all_servers_data = await asyncio.gather(
|
|
137
137
|
self._api_client.get_user_geolocation(),
|
|
138
|
-
self._api_client.get_all_servers()
|
|
139
|
-
self._api_client.get_countries()
|
|
138
|
+
self._api_client.get_all_servers()
|
|
140
139
|
)
|
|
141
|
-
progress.update(task, advance=
|
|
142
|
-
return user_location, all_servers_data
|
|
140
|
+
progress.update(task, advance=2)
|
|
141
|
+
return user_location, all_servers_data
|
|
143
142
|
|
|
144
|
-
async def _process_server_data(self, all_servers_data: List[Dict[str, Any]], user_location: Tuple[float, float]
|
|
143
|
+
async def _process_server_data(self, all_servers_data: List[Dict[str, Any]], user_location: Tuple[float, float]) -> List[Server]:
|
|
145
144
|
loop = asyncio.get_running_loop()
|
|
146
|
-
parse_func = partial(self._parse_server_data, user_location=user_location
|
|
145
|
+
parse_func = partial(self._parse_server_data, user_location=user_location)
|
|
147
146
|
with ThreadPoolExecutor(max_workers=min(32, (os.cpu_count() or 1) + 4)) as executor:
|
|
148
147
|
tasks = [loop.run_in_executor(executor, parse_func, s) for s in all_servers_data]
|
|
149
148
|
processed_servers = await asyncio.gather(*tasks)
|
|
@@ -166,6 +165,8 @@ class ConfigurationOrchestrator:
|
|
|
166
165
|
return info
|
|
167
166
|
|
|
168
167
|
async def _save_all_configurations(self, sorted_servers: List[Server], best_servers: Dict, servers_info: Dict):
|
|
168
|
+
used_paths: Dict[str, int] = {}
|
|
169
|
+
|
|
169
170
|
with self._console.create_progress_bar(transient=False) as progress:
|
|
170
171
|
self.stats.total_configs = len(sorted_servers)
|
|
171
172
|
self.stats.best_configs = len(best_servers)
|
|
@@ -173,18 +174,46 @@ class ConfigurationOrchestrator:
|
|
|
173
174
|
task_all = progress.add_task("Generating standard configs...", total=self.stats.total_configs)
|
|
174
175
|
task_best = progress.add_task("Generating optimized configs...", total=self.stats.best_configs)
|
|
175
176
|
|
|
176
|
-
save_tasks = [
|
|
177
|
-
save_tasks.extend(
|
|
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))
|
|
178
180
|
|
|
179
181
|
await asyncio.gather(*save_tasks)
|
|
180
182
|
async with aiofiles.open(self._output_dir / 'servers.json', 'w') as f:
|
|
181
183
|
await f.write(json.dumps(servers_info, indent=2, separators=(',', ':'), ensure_ascii=False))
|
|
182
184
|
|
|
183
|
-
def
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
188
217
|
|
|
189
218
|
async def _save_config_file(self, config_string: str, path: Path, filename: str, progress, task_id):
|
|
190
219
|
path.mkdir(parents=True, exist_ok=True)
|
|
@@ -194,15 +223,23 @@ class ConfigurationOrchestrator:
|
|
|
194
223
|
progress.update(task_id, advance=1)
|
|
195
224
|
|
|
196
225
|
@staticmethod
|
|
197
|
-
def
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
|
202
236
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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"
|
|
206
243
|
|
|
207
244
|
@staticmethod
|
|
208
245
|
def _generate_wireguard_config_string(server: Server, preferences: UserPreferences, private_key: str) -> str:
|
|
@@ -210,14 +247,11 @@ class ConfigurationOrchestrator:
|
|
|
210
247
|
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}"
|
|
211
248
|
|
|
212
249
|
@staticmethod
|
|
213
|
-
def _parse_server_data(server_data: Dict[str, Any], user_location: Tuple[float, float]
|
|
250
|
+
def _parse_server_data(server_data: Dict[str, Any], user_location: Tuple[float, float]) -> Optional[Server]:
|
|
214
251
|
try:
|
|
215
252
|
location = server_data['locations'][0]
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if not country_code:
|
|
219
|
-
return None
|
|
220
|
-
|
|
253
|
+
country_info = location['country']
|
|
254
|
+
|
|
221
255
|
public_key = next(
|
|
222
256
|
m['value'] for t in server_data['technologies']
|
|
223
257
|
if t['identifier'] == 'wireguard_udp'
|
|
@@ -229,8 +263,8 @@ class ConfigurationOrchestrator:
|
|
|
229
263
|
return Server(
|
|
230
264
|
name=server_data['name'], hostname=server_data['hostname'],
|
|
231
265
|
station=server_data['station'], load=int(server_data.get('load', 0)),
|
|
232
|
-
country=
|
|
233
|
-
city=
|
|
266
|
+
country=country_info['name'], country_code=country_info['code'].lower(),
|
|
267
|
+
city=country_info.get('city', {}).get('name', 'Unknown'),
|
|
234
268
|
latitude=location['latitude'], longitude=location['longitude'],
|
|
235
269
|
public_key=public_key, distance=distance
|
|
236
270
|
)
|
|
@@ -246,9 +280,9 @@ class ConfigurationOrchestrator:
|
|
|
246
280
|
c = 2 * asin(sqrt(a))
|
|
247
281
|
return c * 6371
|
|
248
282
|
|
|
249
|
-
@
|
|
250
|
-
def _sanitize_path_part(part: str) -> str:
|
|
251
|
-
return
|
|
283
|
+
@classmethod
|
|
284
|
+
def _sanitize_path_part(cls, part: str) -> str:
|
|
285
|
+
return part.lower().replace(' ', '_').replace('#', '').translate(cls._path_sanitizer)
|
|
252
286
|
|
|
253
287
|
class Application:
|
|
254
288
|
def __init__(self):
|
|
@@ -301,22 +335,29 @@ class Application:
|
|
|
301
335
|
user_input = self._console.get_preferences(defaults)
|
|
302
336
|
|
|
303
337
|
dns_input = user_input.get("dns")
|
|
304
|
-
|
|
305
|
-
|
|
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
|
+
|
|
306
343
|
use_ip = user_input.get("endpoint_type", "").lower() == 'y'
|
|
307
344
|
|
|
308
|
-
keepalive = defaults.persistent_keepalive
|
|
309
345
|
keepalive_input = user_input.get("keepalive")
|
|
310
346
|
if keepalive_input and keepalive_input.isdigit():
|
|
311
347
|
keepalive_val = int(keepalive_input)
|
|
312
348
|
if 15 <= keepalive_val <= 120:
|
|
313
|
-
|
|
349
|
+
defaults.persistent_keepalive = keepalive_val
|
|
314
350
|
|
|
315
|
-
return UserPreferences(
|
|
351
|
+
return UserPreferences(
|
|
352
|
+
dns=defaults.dns,
|
|
353
|
+
use_ip_for_endpoint=use_ip,
|
|
354
|
+
persistent_keepalive=defaults.persistent_keepalive
|
|
355
|
+
)
|
|
316
356
|
|
|
317
357
|
async def _get_validated_private_key(self, api_client: NordVpnApiClient) -> Optional[str]:
|
|
318
358
|
token = self._console.get_user_input("Please enter your NordVPN access token: ", is_secret=True)
|
|
319
|
-
|
|
359
|
+
is_hex = len(token) == 64 and all(c in '0123456789abcdefABCDEF' for c in token)
|
|
360
|
+
if not is_hex:
|
|
320
361
|
self._console.print_message("error", "Invalid token format.")
|
|
321
362
|
return None
|
|
322
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,90 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: nord-config-generator
|
|
3
|
-
Version: 1.0.2
|
|
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.2
|
|
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.
|
|
File without changes
|
{nord_config_generator-1.0.2 → nord_config_generator-1.0.4}/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
|