ipspot 0.6__py3-none-any.whl → 0.8__py3-none-any.whl
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.
- ipspot/cli.py +24 -15
- ipspot/ipv4.py +99 -28
- ipspot/ipv6.py +93 -22
- ipspot/params.py +13 -4
- ipspot/utils.py +7 -2
- {ipspot-0.6.dist-info → ipspot-0.8.dist-info}/METADATA +47 -25
- ipspot-0.8.dist-info/RECORD +14 -0
- {ipspot-0.6.dist-info → ipspot-0.8.dist-info}/WHEEL +1 -1
- ipspot-0.6.dist-info/RECORD +0 -14
- {ipspot-0.6.dist-info → ipspot-0.8.dist-info}/entry_points.txt +0 -0
- {ipspot-0.6.dist-info → ipspot-0.8.dist-info}/licenses/AUTHORS.md +0 -0
- {ipspot-0.6.dist-info → ipspot-0.8.dist-info}/licenses/LICENSE +0 -0
- {ipspot-0.6.dist-info → ipspot-0.8.dist-info}/top_level.txt +0 -0
ipspot/cli.py
CHANGED
|
@@ -8,9 +8,11 @@ from .ipv6 import get_public_ipv6, get_private_ipv6
|
|
|
8
8
|
from .utils import _filter_parameter
|
|
9
9
|
from .params import IPv4API, IPv6API, PARAMETERS_NAME_MAP
|
|
10
10
|
from .params import IPSPOT_OVERVIEW, IPSPOT_REPO, IPSPOT_VERSION
|
|
11
|
+
from .params import PUBLIC_IPV4_ERROR, PRIVATE_IPV4_ERROR
|
|
12
|
+
from .params import PUBLIC_IPV6_ERROR, PRIVATE_IPV6_ERROR
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
def
|
|
15
|
+
def _print_ipspot_info() -> None: # pragma: no cover
|
|
14
16
|
"""Print ipspot details."""
|
|
15
17
|
tprint("IPSpot")
|
|
16
18
|
tprint("V:" + IPSPOT_VERSION)
|
|
@@ -18,11 +20,13 @@ def ipspot_info() -> None: # pragma: no cover
|
|
|
18
20
|
print("Repo : " + IPSPOT_REPO)
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
def
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
def _print_report(ipv4_api: IPv4API,
|
|
24
|
+
ipv6_api: IPv6API,
|
|
25
|
+
geo: bool,
|
|
26
|
+
timeout: Union[float, Tuple[float, float]],
|
|
27
|
+
max_retries: int,
|
|
28
|
+
retry_delay: float,
|
|
29
|
+
backoff_factor: float) -> None: # pragma: no cover
|
|
26
30
|
"""
|
|
27
31
|
Print collected IP and location data.
|
|
28
32
|
|
|
@@ -32,20 +36,21 @@ def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE,
|
|
|
32
36
|
:param timeout: timeout value for API
|
|
33
37
|
:param max_retries: number of retries
|
|
34
38
|
:param retry_delay: delay between retries (in seconds)
|
|
39
|
+
:param backoff_factor: backoff factor
|
|
35
40
|
"""
|
|
36
41
|
print("Private IP:\n")
|
|
37
42
|
private_ipv4_result = get_private_ipv4()
|
|
38
43
|
if private_ipv4_result["status"]:
|
|
39
44
|
private_ipv4 = private_ipv4_result["data"]["ip"]
|
|
40
45
|
else:
|
|
41
|
-
private_ipv4 =
|
|
46
|
+
private_ipv4 = PRIVATE_IPV4_ERROR
|
|
42
47
|
print(" IPv4: {private_ipv4}\n".format(private_ipv4=private_ipv4))
|
|
43
48
|
|
|
44
49
|
private_ipv6_result = get_private_ipv6()
|
|
45
50
|
if private_ipv6_result["status"]:
|
|
46
51
|
private_ipv6 = private_ipv6_result["data"]["ip"]
|
|
47
52
|
else:
|
|
48
|
-
private_ipv6 =
|
|
53
|
+
private_ipv6 = PRIVATE_IPV6_ERROR
|
|
49
54
|
print(" IPv6: {private_ipv6}".format(private_ipv6=private_ipv6))
|
|
50
55
|
|
|
51
56
|
public_title = "\nPublic IP"
|
|
@@ -59,7 +64,8 @@ def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE,
|
|
|
59
64
|
geo=geo,
|
|
60
65
|
timeout=timeout,
|
|
61
66
|
max_retries=max_retries,
|
|
62
|
-
retry_delay=retry_delay
|
|
67
|
+
retry_delay=retry_delay,
|
|
68
|
+
backoff_factor=backoff_factor)
|
|
63
69
|
if public_ipv4_result["status"]:
|
|
64
70
|
for name, parameter in sorted(public_ipv4_result["data"].items()):
|
|
65
71
|
print(
|
|
@@ -67,7 +73,7 @@ def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE,
|
|
|
67
73
|
name=PARAMETERS_NAME_MAP[name],
|
|
68
74
|
parameter=_filter_parameter(parameter)))
|
|
69
75
|
else:
|
|
70
|
-
print(" Error: {public_ipv4_result
|
|
76
|
+
print(" Error: {public_ipv4_result}".format(public_ipv4_result=PUBLIC_IPV4_ERROR))
|
|
71
77
|
|
|
72
78
|
print("\n IPv6:\n")
|
|
73
79
|
public_ipv6_result = get_public_ipv6(
|
|
@@ -75,7 +81,8 @@ def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE,
|
|
|
75
81
|
geo=geo,
|
|
76
82
|
timeout=timeout,
|
|
77
83
|
max_retries=max_retries,
|
|
78
|
-
retry_delay=retry_delay
|
|
84
|
+
retry_delay=retry_delay,
|
|
85
|
+
backoff_factor=backoff_factor)
|
|
79
86
|
if public_ipv6_result["status"]:
|
|
80
87
|
for name, parameter in sorted(public_ipv6_result["data"].items()):
|
|
81
88
|
print(
|
|
@@ -83,7 +90,7 @@ def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE,
|
|
|
83
90
|
name=PARAMETERS_NAME_MAP[name],
|
|
84
91
|
parameter=_filter_parameter(parameter)))
|
|
85
92
|
else:
|
|
86
|
-
print(" Error: {public_ipv6_result
|
|
93
|
+
print(" Error: {public_ipv6_result}".format(public_ipv6_result=PUBLIC_IPV6_ERROR))
|
|
87
94
|
|
|
88
95
|
|
|
89
96
|
def main() -> None: # pragma: no cover
|
|
@@ -109,20 +116,22 @@ def main() -> None: # pragma: no cover
|
|
|
109
116
|
parser.add_argument('--timeout', help='timeout for the API request', type=float, default=5.0)
|
|
110
117
|
parser.add_argument('--max-retries', help='number of retries', type=int, default=0)
|
|
111
118
|
parser.add_argument('--retry-delay', help='delay between retries (in seconds)', type=float, default=1.0)
|
|
119
|
+
parser.add_argument('--backoff-factor', help='backoff factor', type=float, default=1.0)
|
|
112
120
|
|
|
113
121
|
args = parser.parse_args()
|
|
114
122
|
if args.version:
|
|
115
123
|
print(IPSPOT_VERSION)
|
|
116
124
|
elif args.info:
|
|
117
|
-
|
|
125
|
+
_print_ipspot_info()
|
|
118
126
|
else:
|
|
119
127
|
ipv4_api = IPv4API(args.ipv4_api)
|
|
120
128
|
ipv6_api = IPv6API(args.ipv6_api)
|
|
121
129
|
geo = not args.no_geo
|
|
122
|
-
|
|
130
|
+
_print_report(
|
|
123
131
|
ipv4_api=ipv4_api,
|
|
124
132
|
ipv6_api=ipv6_api,
|
|
125
133
|
geo=geo,
|
|
126
134
|
timeout=args.timeout,
|
|
127
135
|
max_retries=args.max_retries,
|
|
128
|
-
retry_delay=args.retry_delay
|
|
136
|
+
retry_delay=args.retry_delay,
|
|
137
|
+
backoff_factor=args.backoff_factor)
|
ipspot/ipv4.py
CHANGED
|
@@ -36,8 +36,8 @@ def get_private_ipv4() -> Dict[str, Union[bool, Dict[str, str], str]]:
|
|
|
36
36
|
return {"status": False, "error": str(e)}
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def _ip_sb_ipv4(geo: bool
|
|
40
|
-
|
|
39
|
+
def _ip_sb_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
40
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
41
41
|
"""
|
|
42
42
|
Get public IP and geolocation using ip.sb.
|
|
43
43
|
|
|
@@ -64,8 +64,8 @@ def _ip_sb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
64
64
|
return {"status": False, "error": str(e)}
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def _ipleak_net_ipv4(geo: bool
|
|
68
|
-
|
|
67
|
+
def _ipleak_net_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
68
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
69
69
|
"""
|
|
70
70
|
Get public IP and geolocation using ipleak.net.
|
|
71
71
|
|
|
@@ -92,8 +92,8 @@ def _ipleak_net_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
92
92
|
return {"status": False, "error": str(e)}
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
def _my_ip_io_ipv4(geo: bool
|
|
96
|
-
|
|
95
|
+
def _my_ip_io_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
96
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
97
97
|
"""
|
|
98
98
|
Get public IP and geolocation using my-ip.io.
|
|
99
99
|
|
|
@@ -120,8 +120,9 @@ def _my_ip_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
120
120
|
return {"status": False, "error": str(e)}
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
# very low rate limit
|
|
124
|
+
def _ifconfig_co_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
125
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
125
126
|
"""
|
|
126
127
|
Get public IP and geolocation using ifconfig.co.
|
|
127
128
|
|
|
@@ -148,8 +149,8 @@ def _ifconfig_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]
|
|
|
148
149
|
return {"status": False, "error": str(e)}
|
|
149
150
|
|
|
150
151
|
|
|
151
|
-
def _ipapi_co_ipv4(geo: bool
|
|
152
|
-
|
|
152
|
+
def _ipapi_co_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
153
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
153
154
|
"""
|
|
154
155
|
Get public IP and geolocation using ipapi.co.
|
|
155
156
|
|
|
@@ -176,8 +177,8 @@ def _ipapi_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
176
177
|
return {"status": False, "error": str(e)}
|
|
177
178
|
|
|
178
179
|
|
|
179
|
-
def _ip_api_com_ipv4(geo: bool
|
|
180
|
-
|
|
180
|
+
def _ip_api_com_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
181
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
181
182
|
"""
|
|
182
183
|
Get public IP and geolocation using ip-api.com.
|
|
183
184
|
|
|
@@ -206,8 +207,8 @@ def _ip_api_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
206
207
|
return {"status": False, "error": str(e)}
|
|
207
208
|
|
|
208
209
|
|
|
209
|
-
def _ipinfo_io_ipv4(geo: bool
|
|
210
|
-
|
|
210
|
+
def _ipinfo_io_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
211
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
211
212
|
"""
|
|
212
213
|
Get public IP and geolocation using ipinfo.io.
|
|
213
214
|
|
|
@@ -235,8 +236,8 @@ def _ipinfo_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
235
236
|
return {"status": False, "error": str(e)}
|
|
236
237
|
|
|
237
238
|
|
|
238
|
-
def _reallyfreegeoip_org_ipv4(
|
|
239
|
-
|
|
239
|
+
def _reallyfreegeoip_org_ipv4(
|
|
240
|
+
geo: bool, timeout: Union[float, Tuple[float, float]]) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
240
241
|
"""
|
|
241
242
|
Get public IP and geolocation using reallyfreegeoip.org.
|
|
242
243
|
|
|
@@ -263,8 +264,8 @@ def _reallyfreegeoip_org_ipv4(geo: bool=False, timeout: Union[float, Tuple[float
|
|
|
263
264
|
return {"status": False, "error": str(e)}
|
|
264
265
|
|
|
265
266
|
|
|
266
|
-
def _ident_me_ipv4(geo: bool
|
|
267
|
-
|
|
267
|
+
def _ident_me_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
268
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
268
269
|
"""
|
|
269
270
|
Get public IP and geolocation using ident.me.
|
|
270
271
|
|
|
@@ -291,8 +292,8 @@ def _ident_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
291
292
|
return {"status": False, "error": str(e)}
|
|
292
293
|
|
|
293
294
|
|
|
294
|
-
def _tnedi_me_ipv4(geo: bool
|
|
295
|
-
|
|
295
|
+
def _tnedi_me_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
296
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
296
297
|
"""
|
|
297
298
|
Get public IP and geolocation using tnedi.me.
|
|
298
299
|
|
|
@@ -319,7 +320,7 @@ def _tnedi_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
319
320
|
return {"status": False, "error": str(e)}
|
|
320
321
|
|
|
321
322
|
|
|
322
|
-
def _myip_la_ipv4(geo: bool
|
|
323
|
+
def _myip_la_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
323
324
|
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
324
325
|
"""
|
|
325
326
|
Get public IP and geolocation using myip.la.
|
|
@@ -348,7 +349,7 @@ def _myip_la_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
|
|
|
348
349
|
return {"status": False, "error": str(e)}
|
|
349
350
|
|
|
350
351
|
|
|
351
|
-
def _freeipapi_com_ipv4(geo: bool
|
|
352
|
+
def _freeipapi_com_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
352
353
|
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
353
354
|
"""
|
|
354
355
|
Get public IP and geolocation using freeipapi.com.
|
|
@@ -377,7 +378,7 @@ def _freeipapi_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, floa
|
|
|
377
378
|
return {"status": False, "error": str(e)}
|
|
378
379
|
|
|
379
380
|
|
|
380
|
-
def _ipquery_io_ipv4(geo: bool
|
|
381
|
+
def _ipquery_io_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
381
382
|
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
382
383
|
"""
|
|
383
384
|
Get public IP and geolocation using ipquery.io.
|
|
@@ -407,7 +408,7 @@ def _ipquery_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
407
408
|
return {"status": False, "error": str(e)}
|
|
408
409
|
|
|
409
410
|
|
|
410
|
-
def _ipwho_is_ipv4(geo: bool
|
|
411
|
+
def _ipwho_is_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
411
412
|
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
412
413
|
"""
|
|
413
414
|
Get public IP and geolocation using ipwho.is.
|
|
@@ -437,7 +438,7 @@ def _ipwho_is_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
|
|
|
437
438
|
return {"status": False, "error": str(e)}
|
|
438
439
|
|
|
439
440
|
|
|
440
|
-
def _wtfismyip_com_ipv4(geo: bool
|
|
441
|
+
def _wtfismyip_com_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
441
442
|
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
442
443
|
"""
|
|
443
444
|
Get public IP and geolocation using wtfismyip.com.
|
|
@@ -465,6 +466,62 @@ def _wtfismyip_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, floa
|
|
|
465
466
|
return {"status": False, "error": str(e)}
|
|
466
467
|
|
|
467
468
|
|
|
469
|
+
def _myip_wtf_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
470
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
471
|
+
"""
|
|
472
|
+
Get public IP and geolocation using myip.wtf.
|
|
473
|
+
|
|
474
|
+
:param geo: geolocation flag
|
|
475
|
+
:param timeout: timeout value for API
|
|
476
|
+
"""
|
|
477
|
+
try:
|
|
478
|
+
data = _get_json_standard(url="https://json.ipv4.myip.wtf", timeout=timeout)
|
|
479
|
+
result = {"status": True, "data": {"ip": data["YourFuckingIPAddress"], "api": "myip.wtf"}}
|
|
480
|
+
if geo:
|
|
481
|
+
geo_data = {
|
|
482
|
+
"city": data.get("YourFuckingCity"),
|
|
483
|
+
"region": None,
|
|
484
|
+
"country": data.get("YourFuckingCountry"),
|
|
485
|
+
"country_code": data.get("YourFuckingCountryCode"),
|
|
486
|
+
"latitude": None,
|
|
487
|
+
"longitude": None,
|
|
488
|
+
"organization": data.get("YourFuckingISP"),
|
|
489
|
+
"timezone": None
|
|
490
|
+
}
|
|
491
|
+
result["data"].update(geo_data)
|
|
492
|
+
return result
|
|
493
|
+
except Exception as e:
|
|
494
|
+
return {"status": False, "error": str(e)}
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def _db_ip_com_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
498
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
499
|
+
"""
|
|
500
|
+
Get public IP and geolocation using db-ip.com.
|
|
501
|
+
|
|
502
|
+
:param geo: geolocation flag
|
|
503
|
+
:param timeout: timeout value for API
|
|
504
|
+
"""
|
|
505
|
+
try:
|
|
506
|
+
data = _get_json_force_ip(url="https://api.db-ip.com/v2/free/self", timeout=timeout, version="ipv4")
|
|
507
|
+
result = {"status": True, "data": {"ip": data["ipAddress"], "api": "db-ip.com"}}
|
|
508
|
+
if geo:
|
|
509
|
+
geo_data = {
|
|
510
|
+
"city": data.get("city"),
|
|
511
|
+
"region": data.get("stateProv"),
|
|
512
|
+
"country": data.get("countryName"),
|
|
513
|
+
"country_code": data.get("countryCode"),
|
|
514
|
+
"latitude": None, # not provided by free API
|
|
515
|
+
"longitude": None, # not provided by free API
|
|
516
|
+
"organization": None, # not provided by free API
|
|
517
|
+
"timezone": None # not provided by free API
|
|
518
|
+
}
|
|
519
|
+
result["data"].update(geo_data)
|
|
520
|
+
return result
|
|
521
|
+
except Exception as e:
|
|
522
|
+
return {"status": False, "error": str(e)}
|
|
523
|
+
|
|
524
|
+
|
|
468
525
|
IPV4_API_MAP = {
|
|
469
526
|
IPv4API.IFCONFIG_CO: {
|
|
470
527
|
"thread_safe": False,
|
|
@@ -541,13 +598,24 @@ IPV4_API_MAP = {
|
|
|
541
598
|
"geo": True,
|
|
542
599
|
"function": _wtfismyip_com_ipv4
|
|
543
600
|
},
|
|
601
|
+
IPv4API.MYIP_WTF: {
|
|
602
|
+
"thread_safe": True,
|
|
603
|
+
"geo": True,
|
|
604
|
+
"function": _myip_wtf_ipv4
|
|
605
|
+
},
|
|
606
|
+
IPv4API.DB_IP_COM: {
|
|
607
|
+
"thread_safe": False,
|
|
608
|
+
"geo": True,
|
|
609
|
+
"function": _db_ip_com_ipv4
|
|
610
|
+
},
|
|
544
611
|
}
|
|
545
612
|
|
|
546
613
|
|
|
547
|
-
def get_public_ipv4(api: IPv4API=IPv4API.AUTO_SAFE, geo: bool=False,
|
|
548
|
-
timeout: Union[float, Tuple[float, float]]=5,
|
|
614
|
+
def get_public_ipv4(api: IPv4API = IPv4API.AUTO_SAFE, geo: bool = False,
|
|
615
|
+
timeout: Union[float, Tuple[float, float]] = 5,
|
|
549
616
|
max_retries: int = 0,
|
|
550
|
-
retry_delay: float = 1.0
|
|
617
|
+
retry_delay: float = 1.0,
|
|
618
|
+
backoff_factor: float = 1.0) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
551
619
|
"""
|
|
552
620
|
Get public IPv4 and geolocation info based on the selected API.
|
|
553
621
|
|
|
@@ -556,6 +624,7 @@ def get_public_ipv4(api: IPv4API=IPv4API.AUTO_SAFE, geo: bool=False,
|
|
|
556
624
|
:param timeout: timeout value for API
|
|
557
625
|
:param max_retries: number of retries
|
|
558
626
|
:param retry_delay: delay between retries (in seconds)
|
|
627
|
+
:param backoff_factor: backoff factor
|
|
559
628
|
"""
|
|
560
629
|
if api in [IPv4API.AUTO, IPv4API.AUTO_SAFE]:
|
|
561
630
|
for _, api_data in IPV4_API_MAP.items():
|
|
@@ -566,6 +635,7 @@ def get_public_ipv4(api: IPv4API=IPv4API.AUTO_SAFE, geo: bool=False,
|
|
|
566
635
|
func=func,
|
|
567
636
|
max_retries=max_retries,
|
|
568
637
|
retry_delay=retry_delay,
|
|
638
|
+
backoff_factor=backoff_factor,
|
|
569
639
|
geo=geo,
|
|
570
640
|
timeout=timeout)
|
|
571
641
|
if result["status"]:
|
|
@@ -579,6 +649,7 @@ def get_public_ipv4(api: IPv4API=IPv4API.AUTO_SAFE, geo: bool=False,
|
|
|
579
649
|
func=func,
|
|
580
650
|
max_retries=max_retries,
|
|
581
651
|
retry_delay=retry_delay,
|
|
652
|
+
backoff_factor=backoff_factor,
|
|
582
653
|
geo=geo,
|
|
583
654
|
timeout=timeout)
|
|
584
655
|
return {"status": False, "error": "Unsupported API: {api}".format(api=api)}
|
ipspot/ipv6.py
CHANGED
|
@@ -37,8 +37,8 @@ def get_private_ipv6() -> Dict[str, Union[bool, Dict[str, str], str]]:
|
|
|
37
37
|
return {"status": False, "error": str(e)}
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def _ip_sb_ipv6(geo: bool
|
|
41
|
-
|
|
40
|
+
def _ip_sb_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
41
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
42
42
|
"""
|
|
43
43
|
Get public IP and geolocation using ip.sb.
|
|
44
44
|
|
|
@@ -65,8 +65,8 @@ def _ip_sb_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
65
65
|
return {"status": False, "error": str(e)}
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _ident_me_ipv6(geo: bool
|
|
69
|
-
|
|
68
|
+
def _ident_me_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
69
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
70
70
|
"""
|
|
71
71
|
Get public IP and geolocation using ident.me.
|
|
72
72
|
|
|
@@ -93,8 +93,8 @@ def _ident_me_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
93
93
|
return {"status": False, "error": str(e)}
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
def _tnedi_me_ipv6(geo: bool
|
|
97
|
-
|
|
96
|
+
def _tnedi_me_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
97
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
98
98
|
"""
|
|
99
99
|
Get public IP and geolocation using tnedi.me.
|
|
100
100
|
|
|
@@ -121,8 +121,8 @@ def _tnedi_me_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
121
121
|
return {"status": False, "error": str(e)}
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
def _ipleak_net_ipv6(geo: bool
|
|
125
|
-
|
|
124
|
+
def _ipleak_net_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
125
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
126
126
|
"""
|
|
127
127
|
Get public IP and geolocation using ipleak.net.
|
|
128
128
|
|
|
@@ -149,8 +149,8 @@ def _ipleak_net_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
149
149
|
return {"status": False, "error": str(e)}
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
def _my_ip_io_ipv6(geo: bool
|
|
153
|
-
|
|
152
|
+
def _my_ip_io_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
153
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
154
154
|
"""
|
|
155
155
|
Get public IP and geolocation using my-ip.io.
|
|
156
156
|
|
|
@@ -177,8 +177,9 @@ def _my_ip_io_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
177
177
|
return {"status": False, "error": str(e)}
|
|
178
178
|
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
# very low rate limit
|
|
181
|
+
def _ifconfig_co_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
182
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
182
183
|
"""
|
|
183
184
|
Get public IP and geolocation using ifconfig.co.
|
|
184
185
|
|
|
@@ -205,8 +206,8 @@ def _ifconfig_co_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]
|
|
|
205
206
|
return {"status": False, "error": str(e)}
|
|
206
207
|
|
|
207
208
|
|
|
208
|
-
def _reallyfreegeoip_org_ipv6(
|
|
209
|
-
|
|
209
|
+
def _reallyfreegeoip_org_ipv6(
|
|
210
|
+
geo: bool, timeout: Union[float, Tuple[float, float]]) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
210
211
|
"""
|
|
211
212
|
Get public IP and geolocation using reallyfreegeoip.org.
|
|
212
213
|
|
|
@@ -233,8 +234,8 @@ def _reallyfreegeoip_org_ipv6(geo: bool=False, timeout: Union[float, Tuple[float
|
|
|
233
234
|
return {"status": False, "error": str(e)}
|
|
234
235
|
|
|
235
236
|
|
|
236
|
-
def _myip_la_ipv6(geo: bool
|
|
237
|
-
|
|
237
|
+
def _myip_la_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
238
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
238
239
|
"""
|
|
239
240
|
Get public IP and geolocation using myip.la.
|
|
240
241
|
|
|
@@ -262,8 +263,8 @@ def _myip_la_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
|
262
263
|
return {"status": False, "error": str(e)}
|
|
263
264
|
|
|
264
265
|
|
|
265
|
-
def _freeipapi_com_ipv6(geo: bool
|
|
266
|
-
|
|
266
|
+
def _freeipapi_com_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
267
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
267
268
|
"""
|
|
268
269
|
Get public IP and geolocation using freeipapi.com.
|
|
269
270
|
|
|
@@ -291,6 +292,62 @@ def _freeipapi_com_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, floa
|
|
|
291
292
|
return {"status": False, "error": str(e)}
|
|
292
293
|
|
|
293
294
|
|
|
295
|
+
def _wtfismyip_com_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
296
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
297
|
+
"""
|
|
298
|
+
Get public IP and geolocation using wtfismyip.com.
|
|
299
|
+
|
|
300
|
+
:param geo: geolocation flag
|
|
301
|
+
:param timeout: timeout value for API
|
|
302
|
+
"""
|
|
303
|
+
try:
|
|
304
|
+
data = _get_json_standard(url="https://json.ipv6.wtfismyip.com", timeout=timeout)
|
|
305
|
+
result = {"status": True, "data": {"ip": data["YourFuckingIPAddress"], "api": "wtfismyip.com"}}
|
|
306
|
+
if geo:
|
|
307
|
+
geo_data = {
|
|
308
|
+
"city": data.get("YourFuckingCity"),
|
|
309
|
+
"region": None,
|
|
310
|
+
"country": data.get("YourFuckingCountry"),
|
|
311
|
+
"country_code": data.get("YourFuckingCountryCode"),
|
|
312
|
+
"latitude": None,
|
|
313
|
+
"longitude": None,
|
|
314
|
+
"organization": data.get("YourFuckingISP"),
|
|
315
|
+
"timezone": None
|
|
316
|
+
}
|
|
317
|
+
result["data"].update(geo_data)
|
|
318
|
+
return result
|
|
319
|
+
except Exception as e:
|
|
320
|
+
return {"status": False, "error": str(e)}
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _myip_wtf_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
|
|
324
|
+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
325
|
+
"""
|
|
326
|
+
Get public IP and geolocation using myip.wtf.
|
|
327
|
+
|
|
328
|
+
:param geo: geolocation flag
|
|
329
|
+
:param timeout: timeout value for API
|
|
330
|
+
"""
|
|
331
|
+
try:
|
|
332
|
+
data = _get_json_standard(url="https://json.ipv6.myip.wtf", timeout=timeout)
|
|
333
|
+
result = {"status": True, "data": {"ip": data["YourFuckingIPAddress"], "api": "myip.wtf"}}
|
|
334
|
+
if geo:
|
|
335
|
+
geo_data = {
|
|
336
|
+
"city": data.get("YourFuckingCity"),
|
|
337
|
+
"region": None,
|
|
338
|
+
"country": data.get("YourFuckingCountry"),
|
|
339
|
+
"country_code": data.get("YourFuckingCountryCode"),
|
|
340
|
+
"latitude": None,
|
|
341
|
+
"longitude": None,
|
|
342
|
+
"organization": data.get("YourFuckingISP"),
|
|
343
|
+
"timezone": None
|
|
344
|
+
}
|
|
345
|
+
result["data"].update(geo_data)
|
|
346
|
+
return result
|
|
347
|
+
except Exception as e:
|
|
348
|
+
return {"status": False, "error": str(e)}
|
|
349
|
+
|
|
350
|
+
|
|
294
351
|
IPV6_API_MAP = {
|
|
295
352
|
IPv6API.IP_SB: {
|
|
296
353
|
"thread_safe": True,
|
|
@@ -336,14 +393,25 @@ IPV6_API_MAP = {
|
|
|
336
393
|
"thread_safe": False,
|
|
337
394
|
"geo": True,
|
|
338
395
|
"function": _freeipapi_com_ipv6
|
|
339
|
-
}
|
|
396
|
+
},
|
|
397
|
+
IPv6API.WTFISMYIP_COM: {
|
|
398
|
+
"thread_safe": True,
|
|
399
|
+
"geo": True,
|
|
400
|
+
"function": _wtfismyip_com_ipv6
|
|
401
|
+
},
|
|
402
|
+
IPv6API.MYIP_WTF: {
|
|
403
|
+
"thread_safe": True,
|
|
404
|
+
"geo": True,
|
|
405
|
+
"function": _myip_wtf_ipv6
|
|
406
|
+
},
|
|
340
407
|
}
|
|
341
408
|
|
|
342
409
|
|
|
343
|
-
def get_public_ipv6(api: IPv6API=IPv6API.AUTO_SAFE, geo: bool=False,
|
|
344
|
-
timeout: Union[float, Tuple[float, float]]=5,
|
|
410
|
+
def get_public_ipv6(api: IPv6API = IPv6API.AUTO_SAFE, geo: bool = False,
|
|
411
|
+
timeout: Union[float, Tuple[float, float]] = 5,
|
|
345
412
|
max_retries: int = 0,
|
|
346
|
-
retry_delay: float = 1.0
|
|
413
|
+
retry_delay: float = 1.0,
|
|
414
|
+
backoff_factor: float = 1.0) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
347
415
|
"""
|
|
348
416
|
Get public IPv6 and geolocation info based on the selected API.
|
|
349
417
|
|
|
@@ -352,6 +420,7 @@ def get_public_ipv6(api: IPv6API=IPv6API.AUTO_SAFE, geo: bool=False,
|
|
|
352
420
|
:param timeout: timeout value for API
|
|
353
421
|
:param max_retries: number of retries
|
|
354
422
|
:param retry_delay: delay between retries (in seconds)
|
|
423
|
+
:param backoff_factor: backoff factor
|
|
355
424
|
"""
|
|
356
425
|
if api in [IPv6API.AUTO, IPv6API.AUTO_SAFE]:
|
|
357
426
|
for _, api_data in IPV6_API_MAP.items():
|
|
@@ -362,6 +431,7 @@ def get_public_ipv6(api: IPv6API=IPv6API.AUTO_SAFE, geo: bool=False,
|
|
|
362
431
|
func=func,
|
|
363
432
|
max_retries=max_retries,
|
|
364
433
|
retry_delay=retry_delay,
|
|
434
|
+
backoff_factor=backoff_factor,
|
|
365
435
|
geo=geo,
|
|
366
436
|
timeout=timeout)
|
|
367
437
|
if result["status"]:
|
|
@@ -375,6 +445,7 @@ def get_public_ipv6(api: IPv6API=IPv6API.AUTO_SAFE, geo: bool=False,
|
|
|
375
445
|
func=func,
|
|
376
446
|
max_retries=max_retries,
|
|
377
447
|
retry_delay=retry_delay,
|
|
448
|
+
backoff_factor=backoff_factor,
|
|
378
449
|
geo=geo,
|
|
379
450
|
timeout=timeout)
|
|
380
451
|
return {"status": False, "error": "Unsupported API: {api}".format(api=api)}
|
ipspot/params.py
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"""ipspot params."""
|
|
3
3
|
from enum import Enum
|
|
4
4
|
|
|
5
|
-
IPSPOT_VERSION = "0.
|
|
5
|
+
IPSPOT_VERSION = "0.8"
|
|
6
6
|
|
|
7
7
|
IPSPOT_OVERVIEW = '''
|
|
8
|
-
IPSpot is a Python library for retrieving the current system's IP
|
|
9
|
-
It
|
|
10
|
-
|
|
8
|
+
IPSpot is a Python library for retrieving the current system's IP data and detailed location information such as region, longitude, and latitude.
|
|
9
|
+
It supports both public and private IPv4 and IPv6 detection through multiple API providers, using a fallback mechanism for improved reliability.
|
|
10
|
+
It has a simple and modular design, making it easy to perform fast IP, geolocation, provider, and regional lookups directly from your machine.
|
|
11
11
|
'''
|
|
12
12
|
|
|
13
13
|
IPSPOT_REPO = "https://github.com/openscilab/ipspot"
|
|
@@ -38,6 +38,8 @@ class IPv4API(Enum):
|
|
|
38
38
|
IPQUERY_IO = "ipquery.io"
|
|
39
39
|
IPWHO_IS = "ipwho.is"
|
|
40
40
|
WTFISMYIP_COM = "wtfismyip.com"
|
|
41
|
+
MYIP_WTF = "myip.wtf"
|
|
42
|
+
DB_IP_COM = "db-ip.com"
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
class IPv6API(Enum):
|
|
@@ -54,6 +56,8 @@ class IPv6API(Enum):
|
|
|
54
56
|
REALLYFREEGEOIP_ORG = "reallyfreegeoip.org"
|
|
55
57
|
MYIP_LA = "myip.la"
|
|
56
58
|
FREEIPAPI_COM = "freeipapi.com"
|
|
59
|
+
WTFISMYIP_COM = "wtfismyip.com"
|
|
60
|
+
MYIP_WTF = "myip.wtf"
|
|
57
61
|
|
|
58
62
|
|
|
59
63
|
PARAMETERS_NAME_MAP = {
|
|
@@ -68,3 +72,8 @@ PARAMETERS_NAME_MAP = {
|
|
|
68
72
|
"organization": "Organization",
|
|
69
73
|
"api": "API"
|
|
70
74
|
}
|
|
75
|
+
|
|
76
|
+
PUBLIC_IPV4_ERROR = "Unable to retrieve public IPv4 information."
|
|
77
|
+
PRIVATE_IPV4_ERROR = "Unable to retrieve private IPv4 address."
|
|
78
|
+
PUBLIC_IPV6_ERROR = "Unable to retrieve public IPv6 information."
|
|
79
|
+
PRIVATE_IPV6_ERROR = "Unable to retrieve private IPv6 address."
|
ipspot/utils.py
CHANGED
|
@@ -74,22 +74,27 @@ def _get_json_force_ip(url: str, timeout: Union[float, Tuple[float, float]],
|
|
|
74
74
|
def _attempt_with_retries(
|
|
75
75
|
func: Callable,
|
|
76
76
|
max_retries: int,
|
|
77
|
-
retry_delay: float,
|
|
77
|
+
retry_delay: float,
|
|
78
|
+
backoff_factor: float,
|
|
79
|
+
**kwargs: dict) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
78
80
|
"""
|
|
79
81
|
Attempt a function call with retries and delay.
|
|
80
82
|
|
|
81
83
|
:param func: function to execute
|
|
82
84
|
:param max_retries: number of retries
|
|
83
85
|
:param retry_delay: delay between retries (in seconds)
|
|
86
|
+
:param backoff_factor: backoff factor
|
|
84
87
|
:param kwargs: keyword arguments to pass to the function
|
|
85
88
|
"""
|
|
86
89
|
max_retries = max(0, max_retries)
|
|
87
90
|
result = {"status": False, "error": ""}
|
|
91
|
+
next_delay = retry_delay
|
|
88
92
|
for attempt in range(max_retries + 1):
|
|
89
93
|
result = func(**kwargs)
|
|
90
94
|
if result["status"]:
|
|
91
95
|
break
|
|
92
|
-
time.sleep(
|
|
96
|
+
time.sleep(next_delay)
|
|
97
|
+
next_delay *= backoff_factor
|
|
93
98
|
return result
|
|
94
99
|
|
|
95
100
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipspot
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: IPSpot:
|
|
3
|
+
Version: 0.8
|
|
4
|
+
Summary: IPSpot: Retrieve IPv4/IPv6 Addresses with Geolocation Data
|
|
5
5
|
Home-page: https://github.com/openscilab/ipspot
|
|
6
|
-
Download-URL: https://github.com/openscilab/ipspot/tarball/v0.
|
|
6
|
+
Download-URL: https://github.com/openscilab/ipspot/tarball/v0.8
|
|
7
7
|
Author: IPSpot Development Team
|
|
8
8
|
Author-email: ipspot@openscilab.com
|
|
9
9
|
License: MIT
|
|
10
10
|
Project-URL: Source, https://github.com/openscilab/ipspot
|
|
11
|
-
Keywords: ip ipv4 geo geolocation network location ipspot cli
|
|
11
|
+
Keywords: ip ipv4 ipv6 geo geolocation network location ipspot cli
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Natural Language :: English
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
24
|
Classifier: Intended Audience :: Developers
|
|
24
25
|
Classifier: Intended Audience :: Education
|
|
25
26
|
Classifier: Intended Audience :: End Users/Desktop
|
|
@@ -50,7 +51,7 @@ Dynamic: summary
|
|
|
50
51
|
|
|
51
52
|
<div align="center">
|
|
52
53
|
<img src="https://github.com/openscilab/ipspot/raw/main/otherfiles/logo.png" width="350">
|
|
53
|
-
<h1>IPSpot:
|
|
54
|
+
<h1>IPSpot: Retrieve IPv4/IPv6 Addresses with Geolocation Data</h1>
|
|
54
55
|
<br/>
|
|
55
56
|
<a href="https://codecov.io/gh/openscilab/ipspot"><img src="https://codecov.io/gh/openscilab/ipspot/graph/badge.svg?token=XCFKASULS8"></a>
|
|
56
57
|
<a href="https://badge.fury.io/py/ipspot"><img src="https://badge.fury.io/py/ipspot.svg" alt="PyPI version"></a>
|
|
@@ -62,7 +63,7 @@ Dynamic: summary
|
|
|
62
63
|
## Overview
|
|
63
64
|
|
|
64
65
|
<p align="justify">
|
|
65
|
-
<b>IPSpot</b> is a Python library for retrieving the current system's IP
|
|
66
|
+
<b>IPSpot</b> is a Python library for retrieving the current system's IP data and detailed location information such as region, longitude, and latitude. It supports both public and private <b>IPv4</b> and <b>IPv6</b> detection through multiple API providers, using a fallback mechanism for improved reliability. It has a simple and modular design, making it easy to perform fast IP, geolocation, provider, and regional lookups directly from your machine.
|
|
66
67
|
</p>
|
|
67
68
|
|
|
68
69
|
<table>
|
|
@@ -103,13 +104,13 @@ Dynamic: summary
|
|
|
103
104
|
## Installation
|
|
104
105
|
|
|
105
106
|
### Source Code
|
|
106
|
-
- Download [Version 0.
|
|
107
|
+
- Download [Version 0.8](https://github.com/openscilab/ipspot/archive/v0.8.zip) or [Latest Source](https://github.com/openscilab/ipspot/archive/dev.zip)
|
|
107
108
|
- `pip install .`
|
|
108
109
|
|
|
109
110
|
### PyPI
|
|
110
111
|
|
|
111
112
|
- Check [Python Packaging User Guide](https://packaging.python.org/installing/)
|
|
112
|
-
- `pip install ipspot==0.
|
|
113
|
+
- `pip install ipspot==0.8`
|
|
113
114
|
|
|
114
115
|
|
|
115
116
|
## Usage
|
|
@@ -124,7 +125,7 @@ Dynamic: summary
|
|
|
124
125
|
{'status': True, 'data': {'ip': 'xx.xx.xx.xx', 'api': 'ip-api.com'}}
|
|
125
126
|
>>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10)
|
|
126
127
|
{'data': {'country_code': 'GB', 'latitude': 50.9097, 'longitude': -1.4043, 'api': 'ip-api.com', 'country': 'United Kingdom', 'timezone': 'Europe/London', 'organization': '', 'region': 'England', 'ip': 'xx.xx.xx.xx', 'city': 'Southampton'}, 'status': True}
|
|
127
|
-
>>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10, max_retries=5, retry_delay=4)
|
|
128
|
+
>>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10, max_retries=5, retry_delay=4, backoff_factor=1.2)
|
|
128
129
|
{'data': {'country_code': 'GB', 'latitude': 50.9097, 'longitude': -1.4043, 'api': 'ip-api.com', 'country': 'United Kingdom', 'timezone': 'Europe/London', 'organization': '', 'region': 'England', 'ip': 'xx.xx.xx.xx', 'city': 'Southampton'}, 'status': True}
|
|
129
130
|
```
|
|
130
131
|
|
|
@@ -144,7 +145,7 @@ Dynamic: summary
|
|
|
144
145
|
{'data': {'api': 'ip.sb', 'ip': 'xx:xx:xx:xx::xx'}, 'status': True}
|
|
145
146
|
>>> get_public_ipv6(api=IPv6API.IP_SB, geo=True, timeout=10)
|
|
146
147
|
{'data': {'latitude': 51.2993, 'region': None, 'city': None, 'country_code': 'DE', 'api': 'ip.sb', 'longitude': 9.491, 'country': 'Germany', 'organization': 'Hetzner Online', 'timezone': 'Europe/Berlin', 'ip': 'xx:xx:xx:xx::xx'}, 'status': True}
|
|
147
|
-
>>> get_public_ipv6(api=IPv6API.IP_SB, geo=True, timeout=10, max_retries=5, retry_delay=4)
|
|
148
|
+
>>> get_public_ipv6(api=IPv6API.IP_SB, geo=True, timeout=10, max_retries=5, retry_delay=4, backoff_factor=1.2)
|
|
148
149
|
{'data': {'latitude': 51.2993, 'region': None, 'city': None, 'country_code': 'DE', 'api': 'ip.sb', 'longitude': 9.491, 'country': 'Germany', 'organization': 'Hetzner Online', 'timezone': 'Europe/Berlin', 'ip': 'xx:xx:xx:xx::xx'}, 'status': True}
|
|
149
150
|
```
|
|
150
151
|
|
|
@@ -165,7 +166,7 @@ Dynamic: summary
|
|
|
165
166
|
```console
|
|
166
167
|
> ipspot --version
|
|
167
168
|
|
|
168
|
-
0.
|
|
169
|
+
0.8
|
|
169
170
|
```
|
|
170
171
|
|
|
171
172
|
#### Info
|
|
@@ -173,24 +174,24 @@ Dynamic: summary
|
|
|
173
174
|
```console
|
|
174
175
|
> ipspot --info
|
|
175
176
|
|
|
176
|
-
___ ____ ____ _
|
|
177
|
-
|_ _|| _ \ / ___| _ __ ___ | |_
|
|
177
|
+
___ ____ ____ _
|
|
178
|
+
|_ _|| _ \ / ___| _ __ ___ | |_
|
|
178
179
|
| | | |_) |\___ \ | '_ \ / _ \ | __|
|
|
179
|
-
| | | __/ ___) || |_) || (_) || |_
|
|
180
|
+
| | | __/ ___) || |_) || (_) || |_
|
|
180
181
|
|___||_| |____/ | .__/ \___/ \__|
|
|
181
|
-
|_|
|
|
182
|
+
|_|
|
|
182
183
|
|
|
183
|
-
__ __ ___
|
|
184
|
-
\ \ / / _ / _ \
|
|
185
|
-
\ \ / / (_)| | | |
|
|
184
|
+
__ __ ___ ___
|
|
185
|
+
\ \ / / _ / _ \ ( _ )
|
|
186
|
+
\ \ / / (_)| | | | / _ \
|
|
186
187
|
\ V / _ | |_| | _ | (_) |
|
|
187
|
-
\_/ (_) \___/ (_) \___/
|
|
188
|
+
\_/ (_) \___/ (_) \___/
|
|
188
189
|
|
|
189
190
|
|
|
190
191
|
|
|
191
|
-
IPSpot is a Python library for retrieving the current system's IP
|
|
192
|
-
It
|
|
193
|
-
|
|
192
|
+
IPSpot is a Python library for retrieving the current system's IP data and detailed location information such as region, longitude, and latitude.
|
|
193
|
+
It supports both public and private IPv4 and IPv6 detection through multiple API providers, using a fallback mechanism for improved reliability.
|
|
194
|
+
It has a simple and modular design, making it easy to perform fast IP, geolocation, provider, and regional lookups directly from your machine.
|
|
194
195
|
|
|
195
196
|
Repo : https://github.com/openscilab/ipspot
|
|
196
197
|
|
|
@@ -237,7 +238,7 @@ Public IP and Location Info:
|
|
|
237
238
|
|
|
238
239
|
#### IPv4 API
|
|
239
240
|
|
|
240
|
-
ℹ️ `ipv4-api` valid choices: [`auto-safe`, `auto`, `ip-api.com`, `ipinfo.io`, `ip.sb`, `ident.me`, `tnedi.me`, `ipapi.co`, `ipleak.net`, `my-ip.io`, `ifconfig.co`, `reallyfreegeoip.org`, `freeipapi.com`, `myip.la`, `ipquery.io`, `ipwho.is`, `wtfismyip.com`]
|
|
241
|
+
ℹ️ `ipv4-api` valid choices: [`auto-safe`, `auto`, `ip-api.com`, `ipinfo.io`, `ip.sb`, `ident.me`, `tnedi.me`, `ipapi.co`, `ipleak.net`, `my-ip.io`, `ifconfig.co`, `reallyfreegeoip.org`, `freeipapi.com`, `myip.la`, `ipquery.io`, `ipwho.is`, `wtfismyip.com`, `myip.wtf`, `db-ip.com`]
|
|
241
242
|
|
|
242
243
|
ℹ️ The default value: `auto-safe`
|
|
243
244
|
|
|
@@ -280,7 +281,7 @@ Public IP and Location Info:
|
|
|
280
281
|
|
|
281
282
|
#### IPv6 API
|
|
282
283
|
|
|
283
|
-
ℹ️ `ipv6-api` valid choices: [`auto-safe`, `auto`, `ip.sb`, `ident.me`, `tnedi.me`, `ipleak.net`, `my-ip.io`, `ifconfig.co`, `reallyfreegeoip.org`, `myip.la`, `freeipapi.com`]
|
|
284
|
+
ℹ️ `ipv6-api` valid choices: [`auto-safe`, `auto`, `ip.sb`, `ident.me`, `tnedi.me`, `ipleak.net`, `my-ip.io`, `ifconfig.co`, `reallyfreegeoip.org`, `myip.la`, `freeipapi.com`, `wtfismyip.com`, `myip.wtf`]
|
|
284
285
|
|
|
285
286
|
ℹ️ The default value: `auto-safe`
|
|
286
287
|
|
|
@@ -376,6 +377,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
|
376
377
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
377
378
|
|
|
378
379
|
## [Unreleased]
|
|
380
|
+
## [0.8] - 2026-02-05
|
|
381
|
+
### Added
|
|
382
|
+
- Support [wtfismyip.com](https://wtfismyip.com/json) IPv6 API
|
|
383
|
+
- Support [myip.wtf](https://myip.wtf/) IPv6 API
|
|
384
|
+
- Support [myip.wtf](https://myip.wtf/) IPv4 API
|
|
385
|
+
- Support [db-ip.com](https://api.db-ip.com/v2/free/self) IPv4 API
|
|
386
|
+
### Changed
|
|
387
|
+
- `README.md` updated
|
|
388
|
+
## [0.7] - 2025-12-09
|
|
389
|
+
### Added
|
|
390
|
+
- `--backoff-factor` argument
|
|
391
|
+
### Changed
|
|
392
|
+
- CLI messages updated
|
|
393
|
+
- `Python 3.14` added to `test.yml`
|
|
394
|
+
- Internal functions default values removed
|
|
395
|
+
- `README.md` updated
|
|
396
|
+
- Test system modified
|
|
397
|
+
- `ipspot_info` function renamed to `_print_ipspot_info`
|
|
398
|
+
- `display_ip_info` function renamed to `_print_report`
|
|
379
399
|
## [0.6] - 2025-11-18
|
|
380
400
|
### Added
|
|
381
401
|
- `ForceIPHTTPAdapter` class
|
|
@@ -468,7 +488,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
468
488
|
- `--no-geo` argument
|
|
469
489
|
- Logo
|
|
470
490
|
|
|
471
|
-
[Unreleased]: https://github.com/openscilab/ipspot/compare/v0.
|
|
491
|
+
[Unreleased]: https://github.com/openscilab/ipspot/compare/v0.8...dev
|
|
492
|
+
[0.8]: https://github.com/openscilab/ipspot/compare/v0.7...v0.8
|
|
493
|
+
[0.7]: https://github.com/openscilab/ipspot/compare/v0.6...v0.7
|
|
472
494
|
[0.6]: https://github.com/openscilab/ipspot/compare/v0.5...v0.6
|
|
473
495
|
[0.5]: https://github.com/openscilab/ipspot/compare/v0.4...v0.5
|
|
474
496
|
[0.4]: https://github.com/openscilab/ipspot/compare/v0.3...v0.4
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
ipspot/__init__.py,sha256=B47uAOAidLkXps1A4zDig6NN2kB3HcdMC20UOrrEfB4,281
|
|
2
|
+
ipspot/__main__.py,sha256=xKHY_tc94SWktkIHV0O9YADAHPZvT8yXOFSm_L2yMno,105
|
|
3
|
+
ipspot/cli.py,sha256=Y48HA_s6SrTCtjn-iAFNMplAARLwBUx8zpxesG0hARE,4980
|
|
4
|
+
ipspot/ipv4.py,sha256=QPTigPv5bIHX2FkCTZnnuq5-X4_i3_xRlk6d3U6VgfQ,24079
|
|
5
|
+
ipspot/ipv6.py,sha256=wKEOcfGA-y-nuygU9KhqzULzRejEqZ5Dfr_mjHndEoA,16485
|
|
6
|
+
ipspot/params.py,sha256=-j5WYVCWfa_Qdub5q8CELl1S8tq2wg7NbWbW4ilfE9o,2340
|
|
7
|
+
ipspot/utils.py,sha256=W3HJ61_KVtdg4gWr4m9xXblQc_E30rJoF1aKk36Zqzg,4591
|
|
8
|
+
ipspot-0.8.dist-info/licenses/AUTHORS.md,sha256=5ZvxP1KnuVkurB4psQDSqnqiAyn44q1aeiXUsnYgps4,411
|
|
9
|
+
ipspot-0.8.dist-info/licenses/LICENSE,sha256=0aOd4wzZRoSH_35UZXRHS7alPFTtuFEBJrajLuonEIw,1067
|
|
10
|
+
ipspot-0.8.dist-info/METADATA,sha256=QG5WjBJEl7Iz0V2MlmB5G0l_xreUHx79A_ApdrAEPVM,15986
|
|
11
|
+
ipspot-0.8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
ipspot-0.8.dist-info/entry_points.txt,sha256=DJVLepYr8H3UcvWekU5Jy-tcr_xmWrphzgWGNOd3hlk,43
|
|
13
|
+
ipspot-0.8.dist-info/top_level.txt,sha256=v0WgE1z2iCy_bXU53fVcllwHLTvGNBIvq8u3KPC2_Sc,7
|
|
14
|
+
ipspot-0.8.dist-info/RECORD,,
|
ipspot-0.6.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
ipspot/__init__.py,sha256=B47uAOAidLkXps1A4zDig6NN2kB3HcdMC20UOrrEfB4,281
|
|
2
|
-
ipspot/__main__.py,sha256=xKHY_tc94SWktkIHV0O9YADAHPZvT8yXOFSm_L2yMno,105
|
|
3
|
-
ipspot/cli.py,sha256=MdyMh-9miuT8zQgklVrvsimaXnqD17sviWRJbUWlk58,4637
|
|
4
|
-
ipspot/ipv4.py,sha256=ZTv4HFyyxpiF8X-N8Pgk9_aJ7f9T58SpuuB3UKS0WII,21571
|
|
5
|
-
ipspot/ipv6.py,sha256=zqP_ImJQeKpVIhmGQZmPLJsOuviUNnVg70tVhk4UQU0,13991
|
|
6
|
-
ipspot/params.py,sha256=UqhiD8ybTnxRpecZ9RudKXxXyD-j63z23b6Cqbv1LJs,1881
|
|
7
|
-
ipspot/utils.py,sha256=wZxz6e14aDqK0g27AdiRMrbiz8pk4UfxZvbFRDZzhSI,4445
|
|
8
|
-
ipspot-0.6.dist-info/licenses/AUTHORS.md,sha256=5ZvxP1KnuVkurB4psQDSqnqiAyn44q1aeiXUsnYgps4,411
|
|
9
|
-
ipspot-0.6.dist-info/licenses/LICENSE,sha256=0aOd4wzZRoSH_35UZXRHS7alPFTtuFEBJrajLuonEIw,1067
|
|
10
|
-
ipspot-0.6.dist-info/METADATA,sha256=tYTL9nqZZP5YhOzV0IE2Y0CkUAytt-7b_dXWAdUCO08,14879
|
|
11
|
-
ipspot-0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
ipspot-0.6.dist-info/entry_points.txt,sha256=DJVLepYr8H3UcvWekU5Jy-tcr_xmWrphzgWGNOd3hlk,43
|
|
13
|
-
ipspot-0.6.dist-info/top_level.txt,sha256=v0WgE1z2iCy_bXU53fVcllwHLTvGNBIvq8u3KPC2_Sc,7
|
|
14
|
-
ipspot-0.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|