-
Notifications
You must be signed in to change notification settings - Fork 1
/
yspeed.py
480 lines (430 loc) · 18.9 KB
/
yspeed.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
"""Docstring for module Yspeed.
"""
import os
import platform
import sys
import time
import requests
from halo import Halo
from rich.console import Console
from rich.progress import BarColumn, Progress, TextColumn, TimeElapsedColumn
from rich.text import Text
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class Yspeed:
"""
A class that provides methods to retrieve information about the user's,Internet connection speed and IP address.
Methods:
best_server():
Retrieves and returns detailed IP address information using the https://www.speedtest.net/ service.
Information retrieved includes:
Provider: the name of the user's Internet Service Provider,obtained using Selenium.
Server: the name of the server used for the connection,also obtained using Selenium.
This method uses the 'requests' library to make an HTTP request and the 'selenium' library to interact with a web browser to obtain additional information, including the provider name and server name.
get_ip_info():
Retrieves information about the user's public IP address,such as city, region, country, and operator.
This function makes a request to the ipinfo.io service to get information about the user's public IP address.
The information retrieved includes IP address,city, region, country, and operator.
Returns:
dict: A dictionary containing information about the user's public IP address.
get_speedtest():
Retrieves and returns the results of an Internet connection speed test using the online service Speedtest.
The information retrieved includes:
Download Speed: The speed at which data is downloaded from the Internet to the user's computer.
Upload speed: the speed at which data is sent from the user's computer to the Internet.
Ping: the latency of the connection, measured in milliseconds.
This method uses the 'selenium' library to interact with a web browser and automate the speed test on the Speedtest site.
It waits for the test to finish, extracts the results and closes the browser before returning the data.
_extracted_from_get_speedtest():
A private method that initializes a web browser using Selenium and loads the Speedtest site (https://www.speedtest.net/).
This method is used by the 'ipinfo' and 'speedtest' methods to automate interactions with the web site.
get_webdriver(browser):
This method is used to initialize and return an instance of Selenium webdriver based on the browser specified in argument.
It supports Chrome, Firefox and Edge browsers.
"""
def best_server(self) -> dict:
"""
Retrieves and returns detailed IP address information using the https://www.speedtest.net/ service.
Information retrieved includes:
Provider: the name of the user's Internet Service Provider,obtained using Selenium.
Server: the name of the server used for the connection,also obtained using Selenium.
This method uses the 'requests' library to make an HTTP request and the 'selenium' library to interact with a web browser to obtain additional information,
including the provider name and server name.
"""
driver = self._extracted_from_get_speedtest()
wait = WebDriverWait(driver, 5)
wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "hostUrl")))
wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "name")))
provider = driver.find_element(By.CLASS_NAME, "hostUrl").text
server = driver.find_element(By.CLASS_NAME, "name").text
driver.quit()
return {"provider": provider, "Serveur": server}
def get_ip_info(self) -> dict:
"""
Retrieves information about the user's public IP address,such as city, region, country, and operator.
This function makes a request to the ipinfo.io service to get information about the user's public IP address.
The information retrieved includes IP address,city, region, country, and operator.
Returns:
dict: A dictionary containing information about the user's public IP address.
"""
response = requests.get("https://ipinfo.io/json", timeout=5)
data = response.json()
ip_public = data["ip"]
city = data["city"]
region = data["region"]
country = data["country"]
operator = data["org"]
return {
"ip": ip_public,
"city": city,
"region": region,
"country": country,
"operator": operator,
}
def get_speedtest(self) -> dict:
"""
Retrieves and returns the results of an Internet connection speed test using the online service Speedtest.
The information retrieved includes:
Download Speed: The speed at which data is downloaded from the Internet to the user's computer.
Upload speed: the speed at which data is sent from the user's computer to the Internet.
Ping: the latency of the connection, measured in milliseconds.
This method uses the 'selenium' library to interact with a web browser and automate the speed test on the Speedtest site.
It waits for the test to finish,extracts the results and closes the browser before returning the data.
"""
driver = self._extracted_from_get_speedtest()
go_button = driver.find_element(By.CSS_SELECTOR, ".start-button a")
go_button.click()
time.sleep(45)
wait = WebDriverWait(driver, 10)
wait.until(
EC.visibility_of_element_located(
(
By.CLASS_NAME,
"result-data-large.number.result-data-value.download-speed",
)
)
)
wait.until(
EC.visibility_of_element_located(
(
By.CLASS_NAME,
"result-data-large.number.result-data-value.upload-speed",
)
)
)
wait.until(
EC.visibility_of_element_located(
(By.CLASS_NAME, "result-data-value.ping-speed")
)
)
download_speed = driver.find_element(
By.CLASS_NAME, "result-data-large.number.result-data-value.download-speed"
).text
upload_speed = driver.find_element(
By.CLASS_NAME, "result-data-large.number.result-data-value.upload-speed"
).text
ping_speed = driver.find_element(
By.CLASS_NAME, "result-data-value.ping-speed"
).text
driver.quit()
return {
"download": download_speed,
"upload": upload_speed,
"ping": ping_speed,
}
def _extracted_from_get_speedtest(self) -> object:
"""
A private method that initializes a web browser using Selenium and loads the Speedtest site (https://www.speedtest.net/).
"""
result = self._extracted_from_speedtest()
try:
rgpd = result.find_element(By.ID, "onetrust-accept-btn-handler")
rgpd.click()
return result
except NoSuchElementException:
return result
def run_speedtest(self):
"""Returns a dictionary containing the results of the speedtest."""
total_iterations = 50
# Frames pour le spinner personnalisé
spinner_frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
# Utilisez Halo pour créer un spinner personnalisé
try:
with Halo(
spinner={"interval": 100, "frames": spinner_frames},
text="Starting the Speedtest",
color="red",
text_color="yellow",
).start() as spinner:
time.sleep(1)
for _ in range(total_iterations):
spinner.text = "Progression..."
speedtest = self.get_speedtest()
spinner.stop_and_persist(
text="Speedtest completed", symbol="✅".encode("utf-8")
)
return speedtest
except (KeyboardInterrupt, SystemExit):
spinner.stop_and_persist(
text="Speedtest cancelled", symbol="❌".encode("utf-8")
)
return {
"download": "N/A",
"upload": "N/A",
"ping": "N/A",
}
def display_results(self, speedtest: dict) -> None:
"""Displays the results of the speedtest in the console."""
bold_yellow = "bold yellow"
console = Console()
console.print("\nSpeedtest", style=bold_yellow, justify="center")
console.print(
"Download: [bold green]{download}[/bold green]".format(**speedtest),
style="blue",
justify="center",
)
console.print(
"Upload: [bold green]{upload}[/bold green]".format(**speedtest),
style="blue",
justify="center",
)
console.print(
"Ping: [bold green]{ping}[/bold green]".format(**speedtest),
style="blue",
justify="center",
)
console.print("Thanks for Speedtest", style="bold red", justify="center")
def define_brower(self) -> object:
"""
Initialize and return an instance of Selenium webdriver based on the browser specified in argument.
"""
system = platform.system().lower()
if system == "windows":
browser_executables = {
"chrome": "chrome.exe",
"firefox": "firefox.exe",
"edge": "msedge.exe",
}
possible_paths = [
os.path.join(
os.environ["ProgramFiles"], "Google", "Chrome", "Application"
),
os.path.join(
os.environ["ProgramFiles(x86)"], "Google", "Chrome", "Application"
),
os.path.join(os.environ["ProgramFiles"], "Mozilla Firefox"),
os.path.join(os.environ["ProgramFiles(x86)"], "Mozilla Firefox"),
os.path.join(
os.environ["ProgramFiles"], "Microsoft", "Edge", "Application"
),
os.path.join(
os.environ["ProgramFiles(x86)"], "Microsoft", "Edge", "Application"
),
]
elif system == "linux":
browser_executables = {
"chrome": "google-chrome",
"firefox": "firefox",
"edge": "microsoft-edge",
}
possible_paths = [
"/usr/bin",
"/usr/local/bin",
"/opt/google/chrome",
"/opt/microsoft/msedge",
]
else:
raise NotImplementedError(f"Platform '{system}' not supported")
for path in possible_paths:
for browser, executable in browser_executables.items():
if os.path.isfile(os.path.join(path, executable)):
return browser
return None
def _extracted_from_speedtest(self) -> object:
"""
This private method (_extracted_from_speedtest) initializes a web browser using Selenium and loads the Speedtest site(https://www.speedtest.net/).
This method is used by the `ipinfo()` and `speedtest()` methods to automate interactions with the web site.
The main steps of this method are as follows:
Retrieve the browser name from the command line arguments,if available, otherwise use Chrome by default.
Get the instance of the webdriver corresponding to the chosen browser by calling the get_webdriver method.
Load the Speedtest site (https://www.speedtest.net/) using the webdriver.
Wait 5 seconds to allow the site to load correctly.
The method returns the instance of the webdriver initialized and ready to interact with the Speedtest site.
"""
result = self.get_webdriver(self.define_brower())
result.get("https://www.speedtest.net/")
time.sleep(5)
return result
def get_webdriver(self, browser: str) -> object:
"""
This method (get_webdriver) is used to initialize and return an instance of Selenium webdriver based on the browser specified in argument.
It supports Chrome, Firefox and Edge browsers The main steps of this method are as follows:
Check which browser has been specified as an argument.
Configure the webdriver options for the chosen browser,including enabling headless mode and disabling some console message levels.
Return the instance of the webdriver configured for the chosen browser.
If the browser specified in the argument is not supported,the method will throw a ValueError exception.
"""
if browser.lower() == "chrome":
options = webdriver.ChromeOptions()
options.add_argument("--no-sandbox")
options.add_argument("--headless")
options.add_argument("--log-level=3")
# options.add_argument("--disable-dev-shm-usage")
options.add_experimental_option("excludeSwitches", ["enable-logging"])
return webdriver.Chrome(options=options)
if browser.lower() == "firefox":
options = webdriver.FirefoxOptions()
options.add_argument("--headless")
options.log.level = "fatal"
return webdriver.Firefox(options=options)
if browser.lower() == "edge":
options = webdriver.EdgeOptions()
options.add_argument("--headless")
options.add_argument("--log-level=3")
return webdriver.Edge(options=options)
raise ValueError("Browser not supported.")
class TimeElapsedColumnWithLabel(TimeElapsedColumn):
"""A column that displays the time elapsed since the task started."""
def render(self, task) -> Text:
elapsed = task.finished_time if task.finished else task.elapsed
return Text("Time: {:.1f}s".format(elapsed))
def gather_network_info(speedtest: Yspeed, progress: Progress) -> dict:
"""Gathers the network information of the user."""
with progress:
task1 = progress.add_task(
"Getting IP info...", title="[cyan]Getting IP info...", total=1
)
info = speedtest.get_ip_info()
progress.update(task1, advance=1)
task2 = progress.add_task(
"Selecting best server...", title="[cyan]Selecting best server...", total=1
)
best = speedtest.best_server()
progress.update(task2, advance=1)
task3 = progress.add_task(
"Performing speedtest...", title="[cyan]Performing speedtest...", total=1
)
speed = speedtest.get_speedtest()
progress.update(task3, advance=1)
return {
**info,
**best,
**speed,
}
def print_network_info(console: Console, info: dict) -> None:
"""
This function (print_network_info) prints the network information
"""
clear_screen()
bold_yellow = "bold yellow"
console.print("Network Information", style=bold_yellow, justify="center")
console.print(
"Operator: [bold green]{operator}[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print(
"IP: [bold green]{ip}[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print("\nLocalisation", style=bold_yellow, justify="center")
console.print(
"City: [bold green]{city}[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print(
"Region: [bold green]{region}[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print(
"Country: [bold green]{country}[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print("\nBest Server", style=bold_yellow, justify="center")
console.print(
"Provider: [bold green]{provider}[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print(
"Server: [bold green]{Serveur}[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print("\nSpeedTest", style=bold_yellow, justify="center")
console.print(
"Download: [bold green]{download} Mbps[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print(
"Upload: [bold green]{upload} Mbps[/bold green]".format(**info),
style="blue",
justify="center",
)
console.print(
"Ping: [bold green]{ping} ms[/bold green]".format(**info),
style="blue",
justify="center",
)
def _version() -> str:
return "0.1.5"
def author() -> None:
"""
Prints the name of the script author and their contact information.
"""
console = Console()
console.print("Author: Foufou-exe", style="grey35", justify="center")
console.print(
"Github: https://github.com/Foufou-exe", style="grey35", justify="center"
)
console.print(f"Version Yspeed: {_version()}", style="grey35", justify="center")
def clear_screen() -> None:
"""
Clears the terminal screen based on the operating system.
"""
system_name = platform.system()
if system_name == "Windows":
# do something specific for Windows
os.system("cls")
elif system_name == "Linux":
# do something specific for Linux
os.system("clear")
else:
print(f"Unable to clear the terminal because your system is not supported by the program. ({system_name})")
def main() -> None:
"""
This function (main) is the main entry point of the script.
"""
try:
console = Console()
speedtest = Yspeed()
console.print("Welcome to Yspeed!", style="bold yellow", justify="center")
author()
console.print(
"\n\nGathering network information...",
style="bold yellow",
justify="center",
)
with Progress(
TextColumn("{task.fields[title]}"),
BarColumn(),
TimeElapsedColumnWithLabel(),
console=console,
) as progress:
info = gather_network_info(speedtest, progress)
print_network_info(console, info)
except (KeyboardInterrupt, SystemExit):
clear_screen()
console.print("Cancel...", style="bold red", justify="center")
console.print("Goodbye!", style="bold red", justify="center")
sys.exit(0)
if __name__ == "__main__":
main()