ipspot 0.3__py3-none-any.whl → 0.5__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/__init__.py +2 -1
- ipspot/cli.py +70 -15
- ipspot/ipv4.py +492 -122
- ipspot/ipv6.py +245 -0
- ipspot/params.py +29 -6
- ipspot/utils.py +41 -2
- {ipspot-0.3.dist-info → ipspot-0.5.dist-info}/METADATA +167 -30
- ipspot-0.5.dist-info/RECORD +14 -0
- {ipspot-0.3.dist-info → ipspot-0.5.dist-info}/WHEEL +1 -1
- {ipspot-0.3.dist-info → ipspot-0.5.dist-info}/licenses/AUTHORS.md +2 -0
- ipspot-0.3.dist-info/RECORD +0 -13
- {ipspot-0.3.dist-info → ipspot-0.5.dist-info}/entry_points.txt +0 -0
- {ipspot-0.3.dist-info → ipspot-0.5.dist-info}/licenses/LICENSE +0 -0
- {ipspot-0.3.dist-info → ipspot-0.5.dist-info}/top_level.txt +0 -0
ipspot/ipv6.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""ipspot ipv6 functions."""
|
|
3
|
+
import ipaddress
|
|
4
|
+
import socket
|
|
5
|
+
from typing import Union, Dict, List, Tuple
|
|
6
|
+
from .params import IPv6API
|
|
7
|
+
from .utils import is_loopback, _get_json_standard, _attempt_with_retries
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_ipv6(ip: str) -> bool:
|
|
11
|
+
"""
|
|
12
|
+
Check if the given input is a valid IPv6 address.
|
|
13
|
+
|
|
14
|
+
:param ip: input IP
|
|
15
|
+
"""
|
|
16
|
+
if not isinstance(ip, str):
|
|
17
|
+
return False
|
|
18
|
+
try:
|
|
19
|
+
_ = ipaddress.IPv6Address(ip)
|
|
20
|
+
return True
|
|
21
|
+
except Exception:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_private_ipv6() -> Dict[str, Union[bool, Dict[str, str], str]]:
|
|
26
|
+
"""Retrieve the private IPv6 address."""
|
|
27
|
+
try:
|
|
28
|
+
with socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) as s:
|
|
29
|
+
s.connect(("2001:4860:4860::8888", 80))
|
|
30
|
+
private_ip = s.getsockname()[0]
|
|
31
|
+
private_ip = private_ip.split("%")[0]
|
|
32
|
+
if is_ipv6(private_ip) and not is_loopback(private_ip):
|
|
33
|
+
return {"status": True, "data": {"ip": private_ip}}
|
|
34
|
+
return {"status": False, "error": "Could not identify a non-loopback IPv6 address for this system."}
|
|
35
|
+
except Exception as e:
|
|
36
|
+
return {"status": False, "error": str(e)}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _ip_sb_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
40
|
+
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
41
|
+
"""
|
|
42
|
+
Get public IP and geolocation using ip.sb.
|
|
43
|
+
|
|
44
|
+
:param geo: geolocation flag
|
|
45
|
+
:param timeout: timeout value for API
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
data = _get_json_standard(url="https://api-ipv6.ip.sb/geoip", timeout=timeout)
|
|
49
|
+
result = {"status": True, "data": {"ip": data["ip"], "api": "ip.sb"}}
|
|
50
|
+
if geo:
|
|
51
|
+
geo_data = {
|
|
52
|
+
"city": data.get("city"),
|
|
53
|
+
"region": data.get("region"),
|
|
54
|
+
"country": data.get("country"),
|
|
55
|
+
"country_code": data.get("country_code"),
|
|
56
|
+
"latitude": data.get("latitude"),
|
|
57
|
+
"longitude": data.get("longitude"),
|
|
58
|
+
"organization": data.get("organization"),
|
|
59
|
+
"timezone": data.get("timezone")
|
|
60
|
+
}
|
|
61
|
+
result["data"].update(geo_data)
|
|
62
|
+
return result
|
|
63
|
+
except Exception as e:
|
|
64
|
+
return {"status": False, "error": str(e)}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _ident_me_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
68
|
+
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
69
|
+
"""
|
|
70
|
+
Get public IP and geolocation using ident.me.
|
|
71
|
+
|
|
72
|
+
:param geo: geolocation flag
|
|
73
|
+
:param timeout: timeout value for API
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
data = _get_json_standard(url="https://6.ident.me/json", timeout=timeout)
|
|
77
|
+
result = {"status": True, "data": {"ip": data["ip"], "api": "ident.me"}}
|
|
78
|
+
if geo:
|
|
79
|
+
geo_data = {
|
|
80
|
+
"city": data.get("city"),
|
|
81
|
+
"region": None,
|
|
82
|
+
"country": data.get("country"),
|
|
83
|
+
"country_code": data.get("cc"),
|
|
84
|
+
"latitude": data.get("latitude"),
|
|
85
|
+
"longitude": data.get("longitude"),
|
|
86
|
+
"organization": data.get("aso"),
|
|
87
|
+
"timezone": data.get("tz")
|
|
88
|
+
}
|
|
89
|
+
result["data"].update(geo_data)
|
|
90
|
+
return result
|
|
91
|
+
except Exception as e:
|
|
92
|
+
return {"status": False, "error": str(e)}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _tnedi_me_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
96
|
+
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
97
|
+
"""
|
|
98
|
+
Get public IP and geolocation using tnedi.me.
|
|
99
|
+
|
|
100
|
+
:param geo: geolocation flag
|
|
101
|
+
:param timeout: timeout value for API
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
data = _get_json_standard(url="https://6.tnedi.me/json", timeout=timeout)
|
|
105
|
+
result = {"status": True, "data": {"ip": data["ip"], "api": "tnedi.me"}}
|
|
106
|
+
if geo:
|
|
107
|
+
geo_data = {
|
|
108
|
+
"city": data.get("city"),
|
|
109
|
+
"region": None,
|
|
110
|
+
"country": data.get("country"),
|
|
111
|
+
"country_code": data.get("cc"),
|
|
112
|
+
"latitude": data.get("latitude"),
|
|
113
|
+
"longitude": data.get("longitude"),
|
|
114
|
+
"organization": data.get("aso"),
|
|
115
|
+
"timezone": data.get("tz")
|
|
116
|
+
}
|
|
117
|
+
result["data"].update(geo_data)
|
|
118
|
+
return result
|
|
119
|
+
except Exception as e:
|
|
120
|
+
return {"status": False, "error": str(e)}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _ipleak_net_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
124
|
+
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
125
|
+
"""
|
|
126
|
+
Get public IP and geolocation using ipleak.net.
|
|
127
|
+
|
|
128
|
+
:param geo: geolocation flag
|
|
129
|
+
:param timeout: timeout value for API
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
data = _get_json_standard(url="https://ipv6.ipleak.net/json/", timeout=timeout)
|
|
133
|
+
result = {"status": True, "data": {"ip": data["ip"], "api": "ipleak.net"}}
|
|
134
|
+
if geo:
|
|
135
|
+
geo_data = {
|
|
136
|
+
"city": data.get("city_name"),
|
|
137
|
+
"region": data.get("region_name"),
|
|
138
|
+
"country": data.get("country_name"),
|
|
139
|
+
"country_code": data.get("country_code"),
|
|
140
|
+
"latitude": data.get("latitude"),
|
|
141
|
+
"longitude": data.get("longitude"),
|
|
142
|
+
"organization": data.get("isp_name"),
|
|
143
|
+
"timezone": data.get("time_zone")
|
|
144
|
+
}
|
|
145
|
+
result["data"].update(geo_data)
|
|
146
|
+
return result
|
|
147
|
+
except Exception as e:
|
|
148
|
+
return {"status": False, "error": str(e)}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _my_ip_io_ipv6(geo: bool=False, timeout: Union[float, Tuple[float, float]]
|
|
152
|
+
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
153
|
+
"""
|
|
154
|
+
Get public IP and geolocation using my-ip.io.
|
|
155
|
+
|
|
156
|
+
:param geo: geolocation flag
|
|
157
|
+
:param timeout: timeout value for API
|
|
158
|
+
"""
|
|
159
|
+
try:
|
|
160
|
+
data = _get_json_standard(url="https://api6.my-ip.io/v2/ip.json", timeout=timeout)
|
|
161
|
+
result = {"status": True, "data": {"ip": data["ip"], "api": "my-ip.io"}}
|
|
162
|
+
if geo:
|
|
163
|
+
geo_data = {
|
|
164
|
+
"city": data.get("city"),
|
|
165
|
+
"region": data.get("region"),
|
|
166
|
+
"country": data.get("country", {}).get("name"),
|
|
167
|
+
"country_code": data.get("country", {}).get("code"),
|
|
168
|
+
"latitude": data.get("location", {}).get("lat"),
|
|
169
|
+
"longitude": data.get("location", {}).get("lon"),
|
|
170
|
+
"organization": data.get("asn", {}).get("name"),
|
|
171
|
+
"timezone": data.get("timeZone")
|
|
172
|
+
}
|
|
173
|
+
result["data"].update(geo_data)
|
|
174
|
+
return result
|
|
175
|
+
except Exception as e:
|
|
176
|
+
return {"status": False, "error": str(e)}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
IPV6_API_MAP = {
|
|
180
|
+
IPv6API.IP_SB: {
|
|
181
|
+
"thread_safe": True,
|
|
182
|
+
"geo": True,
|
|
183
|
+
"function": _ip_sb_ipv6
|
|
184
|
+
},
|
|
185
|
+
IPv6API.IDENT_ME: {
|
|
186
|
+
"thread_safe": True,
|
|
187
|
+
"geo": True,
|
|
188
|
+
"function": _ident_me_ipv6
|
|
189
|
+
},
|
|
190
|
+
IPv6API.TNEDI_ME: {
|
|
191
|
+
"thread_safe": True,
|
|
192
|
+
"geo": True,
|
|
193
|
+
"function": _tnedi_me_ipv6
|
|
194
|
+
},
|
|
195
|
+
IPv6API.IPLEAK_NET: {
|
|
196
|
+
"thread_safe": True,
|
|
197
|
+
"geo": True,
|
|
198
|
+
"function": _ipleak_net_ipv6
|
|
199
|
+
},
|
|
200
|
+
IPv6API.MY_IP_IO: {
|
|
201
|
+
"thread_safe": True,
|
|
202
|
+
"geo": True,
|
|
203
|
+
"function": _my_ip_io_ipv6
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_public_ipv6(api: IPv6API=IPv6API.AUTO_SAFE, geo: bool=False,
|
|
209
|
+
timeout: Union[float, Tuple[float, float]]=5,
|
|
210
|
+
max_retries: int = 0,
|
|
211
|
+
retry_delay: float = 1.0) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
212
|
+
"""
|
|
213
|
+
Get public IPv6 and geolocation info based on the selected API.
|
|
214
|
+
|
|
215
|
+
:param api: public IPv6 API
|
|
216
|
+
:param geo: geolocation flag
|
|
217
|
+
:param timeout: timeout value for API
|
|
218
|
+
:param max_retries: number of retries
|
|
219
|
+
:param retry_delay: delay between retries (in seconds)
|
|
220
|
+
"""
|
|
221
|
+
if api in [IPv6API.AUTO, IPv6API.AUTO_SAFE]:
|
|
222
|
+
for _, api_data in IPV6_API_MAP.items():
|
|
223
|
+
if api == IPv6API.AUTO_SAFE and not api_data["thread_safe"]:
|
|
224
|
+
continue
|
|
225
|
+
func = api_data["function"]
|
|
226
|
+
result = _attempt_with_retries(
|
|
227
|
+
func=func,
|
|
228
|
+
max_retries=max_retries,
|
|
229
|
+
retry_delay=retry_delay,
|
|
230
|
+
geo=geo,
|
|
231
|
+
timeout=timeout)
|
|
232
|
+
if result["status"]:
|
|
233
|
+
return result
|
|
234
|
+
return {"status": False, "error": "All attempts failed."}
|
|
235
|
+
else:
|
|
236
|
+
api_data = IPV6_API_MAP.get(api)
|
|
237
|
+
if api_data:
|
|
238
|
+
func = api_data["function"]
|
|
239
|
+
return _attempt_with_retries(
|
|
240
|
+
func=func,
|
|
241
|
+
max_retries=max_retries,
|
|
242
|
+
retry_delay=retry_delay,
|
|
243
|
+
geo=geo,
|
|
244
|
+
timeout=timeout)
|
|
245
|
+
return {"status": False, "error": "Unsupported API: {api}".format(api=api)}
|
ipspot/params.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""ipspot params."""
|
|
3
3
|
from enum import Enum
|
|
4
4
|
|
|
5
|
-
IPSPOT_VERSION = "0.
|
|
5
|
+
IPSPOT_VERSION = "0.5"
|
|
6
6
|
|
|
7
7
|
IPSPOT_OVERVIEW = '''
|
|
8
8
|
IPSpot is a Python library for retrieving the current system's IP address and location information.
|
|
@@ -22,11 +22,34 @@ class IPv4API(Enum):
|
|
|
22
22
|
"""Public IPv4 API enum."""
|
|
23
23
|
|
|
24
24
|
AUTO = "auto"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
AUTO_SAFE = "auto-safe"
|
|
26
|
+
IP_API_COM = "ip-api.com"
|
|
27
|
+
IPAPI_CO = "ipapi.co"
|
|
28
|
+
IPINFO_IO = "ipinfo.io"
|
|
29
|
+
IP_SB = "ip.sb"
|
|
30
|
+
IDENT_ME = "ident.me"
|
|
31
|
+
TNEDI_ME = "tnedi.me"
|
|
32
|
+
IPLEAK_NET = "ipleak.net"
|
|
33
|
+
MY_IP_IO = "my-ip.io"
|
|
34
|
+
IFCONFIG_CO = "ifconfig.co"
|
|
35
|
+
REALLYFREEGEOIP_ORG = "reallyfreegeoip.org"
|
|
36
|
+
MYIP_LA = "myip.la"
|
|
37
|
+
FREEIPAPI_COM = "freeipapi.com"
|
|
38
|
+
IPQUERY_IO = "ipquery.io"
|
|
39
|
+
IPWHO_IS = "ipwho.is"
|
|
40
|
+
WTFISMYIP_COM = "wtfismyip.com"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class IPv6API(Enum):
|
|
44
|
+
"""Public IPv6 API enum."""
|
|
45
|
+
|
|
46
|
+
AUTO = "auto"
|
|
47
|
+
AUTO_SAFE = "auto-safe"
|
|
48
|
+
IP_SB = "ip.sb"
|
|
49
|
+
IDENT_ME = "ident.me"
|
|
50
|
+
TNEDI_ME = "tnedi.me"
|
|
51
|
+
IPLEAK_NET = "ipleak.net"
|
|
52
|
+
MY_IP_IO = "my-ip.io"
|
|
30
53
|
|
|
31
54
|
|
|
32
55
|
PARAMETERS_NAME_MAP = {
|
ipspot/utils.py
CHANGED
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""ipspot utils."""
|
|
3
|
+
import time
|
|
3
4
|
import ipaddress
|
|
4
|
-
|
|
5
|
+
import requests
|
|
6
|
+
from typing import Callable, Dict
|
|
7
|
+
from typing import Union, Tuple, Any
|
|
8
|
+
from .params import REQUEST_HEADERS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _attempt_with_retries(
|
|
12
|
+
func: Callable,
|
|
13
|
+
max_retries: int,
|
|
14
|
+
retry_delay: float, **kwargs: dict) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
|
|
15
|
+
"""
|
|
16
|
+
Attempt a function call with retries and delay.
|
|
17
|
+
|
|
18
|
+
:param func: function to execute
|
|
19
|
+
:param max_retries: number of retries
|
|
20
|
+
:param retry_delay: delay between retries (in seconds)
|
|
21
|
+
:param kwargs: keyword arguments to pass to the function
|
|
22
|
+
"""
|
|
23
|
+
max_retries = max(0, max_retries)
|
|
24
|
+
result = {"status": False, "error": ""}
|
|
25
|
+
for attempt in range(max_retries + 1):
|
|
26
|
+
result = func(**kwargs)
|
|
27
|
+
if result["status"]:
|
|
28
|
+
break
|
|
29
|
+
time.sleep(retry_delay)
|
|
30
|
+
return result
|
|
5
31
|
|
|
6
32
|
|
|
7
33
|
def is_loopback(ip: str) -> bool:
|
|
@@ -17,7 +43,7 @@ def is_loopback(ip: str) -> bool:
|
|
|
17
43
|
return False
|
|
18
44
|
|
|
19
45
|
|
|
20
|
-
def
|
|
46
|
+
def _filter_parameter(parameter: Any) -> Any:
|
|
21
47
|
"""
|
|
22
48
|
Filter input parameter.
|
|
23
49
|
|
|
@@ -28,3 +54,16 @@ def filter_parameter(parameter: Any) -> Any:
|
|
|
28
54
|
if isinstance(parameter, str) and len(parameter.strip()) == 0:
|
|
29
55
|
return "N/A"
|
|
30
56
|
return parameter
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _get_json_standard(url: str, timeout: Union[float, Tuple[float, float]]) -> dict:
|
|
60
|
+
"""
|
|
61
|
+
Send standard GET request that returns JSON response.
|
|
62
|
+
|
|
63
|
+
:param url: API url
|
|
64
|
+
:param timeout: timeout value for API
|
|
65
|
+
"""
|
|
66
|
+
with requests.Session() as session:
|
|
67
|
+
response = session.get(url, headers=REQUEST_HEADERS, timeout=timeout)
|
|
68
|
+
response.raise_for_status()
|
|
69
|
+
return response.json()
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipspot
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5
|
|
4
4
|
Summary: IPSpot: A Python Tool to Fetch the System's IP Address
|
|
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.5
|
|
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
11
|
Keywords: ip ipv4 geo geolocation network location ipspot cli
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Natural Language :: English
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
@@ -102,13 +102,13 @@ Dynamic: summary
|
|
|
102
102
|
## Installation
|
|
103
103
|
|
|
104
104
|
### Source Code
|
|
105
|
-
- Download [Version 0.
|
|
105
|
+
- Download [Version 0.5](https://github.com/openscilab/ipspot/archive/v0.5.zip) or [Latest Source](https://github.com/openscilab/ipspot/archive/dev.zip)
|
|
106
106
|
- `pip install .`
|
|
107
107
|
|
|
108
108
|
### PyPI
|
|
109
109
|
|
|
110
110
|
- Check [Python Packaging User Guide](https://packaging.python.org/installing/)
|
|
111
|
-
- `pip install ipspot==0.
|
|
111
|
+
- `pip install ipspot==0.5`
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
## Usage
|
|
@@ -119,9 +119,11 @@ Dynamic: summary
|
|
|
119
119
|
|
|
120
120
|
```pycon
|
|
121
121
|
>>> from ipspot import get_public_ipv4, IPv4API
|
|
122
|
-
>>> get_public_ipv4(api=IPv4API.
|
|
122
|
+
>>> get_public_ipv4(api=IPv4API.IP_API_COM)
|
|
123
123
|
{'status': True, 'data': {'ip': 'xx.xx.xx.xx', 'api': 'ip-api.com'}}
|
|
124
|
-
>>> get_public_ipv4(api=IPv4API.
|
|
124
|
+
>>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10)
|
|
125
|
+
{'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}
|
|
126
|
+
>>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10, max_retries=5, retry_delay=4)
|
|
125
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}
|
|
126
128
|
```
|
|
127
129
|
|
|
@@ -133,6 +135,26 @@ Dynamic: summary
|
|
|
133
135
|
{'status': True, 'data': {'ip': '10.36.18.154'}}
|
|
134
136
|
```
|
|
135
137
|
|
|
138
|
+
#### Public IPv6
|
|
139
|
+
|
|
140
|
+
```pycon
|
|
141
|
+
>>> from ipspot import get_public_ipv6, IPv6API
|
|
142
|
+
>>> get_public_ipv6(api=IPv6API.IP_SB)
|
|
143
|
+
{'data': {'api': 'ip.sb', 'ip': 'xx:xx:xx:xx::xx'}, 'status': True}
|
|
144
|
+
>>> get_public_ipv6(api=IPv6API.IP_SB, geo=True, timeout=10)
|
|
145
|
+
{'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}
|
|
146
|
+
>>> get_public_ipv6(api=IPv6API.IP_SB, geo=True, timeout=10, max_retries=5, retry_delay=4)
|
|
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}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Private IPv6
|
|
151
|
+
|
|
152
|
+
```pycon
|
|
153
|
+
>>> from ipspot import get_private_ipv6
|
|
154
|
+
>>> get_private_ipv6()
|
|
155
|
+
{'status': True, 'data': {'ip': 'fe80::e1bd:f78:b233:21c9'}}
|
|
156
|
+
```
|
|
157
|
+
|
|
136
158
|
### CLI
|
|
137
159
|
|
|
138
160
|
ℹ️ You can use `ipspot` or `python -m ipspot` to run this program
|
|
@@ -142,7 +164,7 @@ Dynamic: summary
|
|
|
142
164
|
```console
|
|
143
165
|
> ipspot --version
|
|
144
166
|
|
|
145
|
-
0.
|
|
167
|
+
0.5
|
|
146
168
|
```
|
|
147
169
|
|
|
148
170
|
#### Info
|
|
@@ -157,9 +179,9 @@ Dynamic: summary
|
|
|
157
179
|
|___||_| |____/ | .__/ \___/ \__|
|
|
158
180
|
|_|
|
|
159
181
|
|
|
160
|
-
__ __ ___
|
|
161
|
-
\ \ / / _ / _ \ |___
|
|
162
|
-
\ \ / / (_)| | | |
|
|
182
|
+
__ __ ___ ____
|
|
183
|
+
\ \ / / _ / _ \ | ___|
|
|
184
|
+
\ \ / / (_)| | | | |___ \
|
|
163
185
|
\ V / _ | |_| | _ ___) |
|
|
164
186
|
\_/ (_) \___/ (_)|____/
|
|
165
187
|
|
|
@@ -179,7 +201,9 @@ Repo : https://github.com/openscilab/ipspot
|
|
|
179
201
|
> ipspot
|
|
180
202
|
Private IP:
|
|
181
203
|
|
|
182
|
-
|
|
204
|
+
IPv4: 192.168.1.35
|
|
205
|
+
|
|
206
|
+
IPv6: fe80::e1bd:f78:b233:21c9
|
|
183
207
|
|
|
184
208
|
Public IP and Location Info:
|
|
185
209
|
|
|
@@ -197,28 +221,88 @@ Public IP and Location Info:
|
|
|
197
221
|
|
|
198
222
|
#### IPv4 API
|
|
199
223
|
|
|
200
|
-
ℹ️ `ipv4-api` valid choices: [`auto`, `
|
|
224
|
+
ℹ️ `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`]
|
|
201
225
|
|
|
202
|
-
ℹ️ The default value: `auto`
|
|
226
|
+
ℹ️ The default value: `auto-safe`
|
|
203
227
|
|
|
204
228
|
```console
|
|
205
|
-
> ipspot --ipv4-api="ipinfo"
|
|
229
|
+
> ipspot --ipv4-api="ipinfo.io"
|
|
206
230
|
Private IP:
|
|
207
231
|
|
|
208
|
-
|
|
232
|
+
IPv4: 192.168.1.35
|
|
233
|
+
|
|
234
|
+
IPv6: fe80::e1bd:f78:b233:21c9
|
|
209
235
|
|
|
210
236
|
Public IP and Location Info:
|
|
211
237
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
238
|
+
IPv4:
|
|
239
|
+
|
|
240
|
+
API: ipinfo.io
|
|
241
|
+
City: Nuremberg
|
|
242
|
+
Country: Germany
|
|
243
|
+
Country Code: DE
|
|
244
|
+
IP: xx.xx.xx.xx
|
|
245
|
+
Latitude: 49.4527
|
|
246
|
+
Longitude: 11.0783
|
|
247
|
+
Organization: Hetzner Online GmbH
|
|
248
|
+
Region: Bavaria
|
|
249
|
+
Timezone: Europe/Berlin
|
|
250
|
+
|
|
251
|
+
IPv6:
|
|
252
|
+
|
|
253
|
+
API: ip.sb
|
|
254
|
+
City: N/A
|
|
255
|
+
Country: Germany
|
|
256
|
+
Country Code: DE
|
|
257
|
+
IP: xx:xx:xx:xx::xx
|
|
258
|
+
Latitude: 51.2993
|
|
259
|
+
Longitude: 9.491
|
|
260
|
+
Organization: Hetzner Online
|
|
261
|
+
Region: N/A
|
|
262
|
+
Timezone: Europe/Berlin
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### IPv6 API
|
|
266
|
+
|
|
267
|
+
ℹ️ `ipv6-api` valid choices: [`auto-safe`, `auto`, `ip.sb`, `ident.me`, `tnedi.me`, `ipleak.net`, `my-ip.io`]
|
|
268
|
+
|
|
269
|
+
ℹ️ The default value: `auto-safe`
|
|
270
|
+
|
|
271
|
+
```console
|
|
272
|
+
> ipspot --ipv6-api="ip.sb"
|
|
273
|
+
Private IP:
|
|
274
|
+
|
|
275
|
+
IPv4: 192.168.1.35
|
|
276
|
+
|
|
277
|
+
IPv6: fe80::e1bd:f78:b233:21c9
|
|
278
|
+
|
|
279
|
+
Public IP and Location Info:
|
|
280
|
+
|
|
281
|
+
IPv4:
|
|
282
|
+
|
|
283
|
+
API: ipinfo.io
|
|
284
|
+
City: Nuremberg
|
|
285
|
+
Country: Germany
|
|
286
|
+
Country Code: DE
|
|
287
|
+
IP: xx.xx.xx.xx
|
|
288
|
+
Latitude: 49.4527
|
|
289
|
+
Longitude: 11.0783
|
|
290
|
+
Organization: Hetzner Online GmbH
|
|
291
|
+
Region: Bavaria
|
|
292
|
+
Timezone: Europe/Berlin
|
|
293
|
+
|
|
294
|
+
IPv6:
|
|
295
|
+
|
|
296
|
+
API: ip.sb
|
|
297
|
+
City: N/A
|
|
298
|
+
Country: Germany
|
|
299
|
+
Country Code: DE
|
|
300
|
+
IP: xx:xx:xx:xx::xx
|
|
301
|
+
Latitude: 51.2993
|
|
302
|
+
Longitude: 9.491
|
|
303
|
+
Organization: Hetzner Online
|
|
304
|
+
Region: N/A
|
|
305
|
+
Timezone: Europe/Berlin
|
|
222
306
|
```
|
|
223
307
|
|
|
224
308
|
#### No Geolocation
|
|
@@ -227,12 +311,21 @@ Public IP and Location Info:
|
|
|
227
311
|
> ipspot --no-geo
|
|
228
312
|
Private IP:
|
|
229
313
|
|
|
230
|
-
|
|
314
|
+
IPv4: 192.168.1.35
|
|
315
|
+
|
|
316
|
+
IPv6: fe80::5c40:769f:22de:c196
|
|
231
317
|
|
|
232
318
|
Public IP:
|
|
233
319
|
|
|
234
|
-
|
|
235
|
-
|
|
320
|
+
IPv4:
|
|
321
|
+
|
|
322
|
+
API: tnedi.me
|
|
323
|
+
IP: xx.xx.xx.xx
|
|
324
|
+
|
|
325
|
+
IPv6:
|
|
326
|
+
|
|
327
|
+
API: ip.sb
|
|
328
|
+
IP: xx:xx:xx:xx::xx
|
|
236
329
|
```
|
|
237
330
|
|
|
238
331
|
## Issues & Bug Reports
|
|
@@ -267,6 +360,48 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
|
267
360
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
268
361
|
|
|
269
362
|
## [Unreleased]
|
|
363
|
+
## [0.5] - 2025-10-17
|
|
364
|
+
### Added
|
|
365
|
+
- `setup-warp` action
|
|
366
|
+
- Support [ipwho.is](https://ipwho.is/)
|
|
367
|
+
- Support [ipquery.io](http://api.ipquery.io/?format=json)
|
|
368
|
+
- Support [wtfismyip.com](https://wtfismyip.com/json)
|
|
369
|
+
- Support [ident.me](https://ident.me/json) IPv6 API
|
|
370
|
+
- Support [tnedi.me](https://tnedi.me/json) IPv6 API
|
|
371
|
+
- Support [ip.sb](https://api.ip.sb/geoip) IPv6 API
|
|
372
|
+
- Support [ipleak.net](https://ipleak.net/json/) IPv6 API
|
|
373
|
+
- Support [my-ip.io](https://www.my-ip.io/) IPv6 API
|
|
374
|
+
- `is_ipv6` function
|
|
375
|
+
- `get_private_ipv6` function
|
|
376
|
+
- `get_public_ipv6` function
|
|
377
|
+
- `IPv6API` enum
|
|
378
|
+
- `--ipv6-api` argument
|
|
379
|
+
### Changed
|
|
380
|
+
- Test system modified
|
|
381
|
+
- `README.md` updated
|
|
382
|
+
## [0.4] - 2025-06-09
|
|
383
|
+
### Added
|
|
384
|
+
- Support [ipapi.co](https://ipapi.co/json/)
|
|
385
|
+
- Support [ipleak.net](https://ipleak.net/json/)
|
|
386
|
+
- Support [my-ip.io](https://www.my-ip.io/)
|
|
387
|
+
- Support [ifconfig.co](https://ifconfig.co/json)
|
|
388
|
+
- Support [reallyfreegeoip.org](https://reallyfreegeoip.org/json/)
|
|
389
|
+
- Support [myip.la](https://api.myip.la/en?json)
|
|
390
|
+
- Support [freeipapi.com](https://freeipapi.com/api/json/)
|
|
391
|
+
- `AUTO_SAFE` mode
|
|
392
|
+
- `_get_json_standard` function
|
|
393
|
+
- `_get_json_ipv4_forced` function
|
|
394
|
+
- `--max-retries` argument
|
|
395
|
+
- `--retry-delay` argument
|
|
396
|
+
### Changed
|
|
397
|
+
- `IPv4API.IPAPI` renamed to `IPv4API.IP_API_COM`
|
|
398
|
+
- `IPv4API.IPINFO` renamed to `IPv4API.IPINFO_IO`
|
|
399
|
+
- `IPv4API.IPSB` renamed to `IPv4API.IP_SB`
|
|
400
|
+
- `IPv4API.IDENTME` renamed to `IPv4API.IDENT_ME`
|
|
401
|
+
- `IPv4API.TNEDIME` renamed to `IPv4API.TNEDI_ME`
|
|
402
|
+
- `get_public_ipv4` function modified
|
|
403
|
+
- `filter_parameter` function renamed to `_filter_parameter`
|
|
404
|
+
- `README.md` updated
|
|
270
405
|
## [0.3] - 2025-05-19
|
|
271
406
|
### Added
|
|
272
407
|
- `is_ipv4` function
|
|
@@ -303,7 +438,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
303
438
|
- `--no-geo` argument
|
|
304
439
|
- Logo
|
|
305
440
|
|
|
306
|
-
[Unreleased]: https://github.com/openscilab/ipspot/compare/v0.
|
|
441
|
+
[Unreleased]: https://github.com/openscilab/ipspot/compare/v0.5...dev
|
|
442
|
+
[0.5]: https://github.com/openscilab/ipspot/compare/v0.4...v0.5
|
|
443
|
+
[0.4]: https://github.com/openscilab/ipspot/compare/v0.3...v0.4
|
|
307
444
|
[0.3]: https://github.com/openscilab/ipspot/compare/v0.2...v0.3
|
|
308
445
|
[0.2]: https://github.com/openscilab/ipspot/compare/v0.1...v0.2
|
|
309
446
|
[0.1]: https://github.com/openscilab/ipspot/compare/3216fb7...v0.1
|
|
@@ -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=MdyMh-9miuT8zQgklVrvsimaXnqD17sviWRJbUWlk58,4637
|
|
4
|
+
ipspot/ipv4.py,sha256=-HGv-yAsFI56I06qNFDquVVGP9ZhfWwih1Vs3JtemZQ,23825
|
|
5
|
+
ipspot/ipv6.py,sha256=svGgydUUSoXeuT2hLgi5CDDy3n2PtV-b6uDvP5t_iKI,8754
|
|
6
|
+
ipspot/params.py,sha256=PVnz3F1jB5aIle_fScGoF-0MfREhMxcTCXGEXmimUQs,1741
|
|
7
|
+
ipspot/utils.py,sha256=YtkyWGljpFc72rSGljsARjlVrF9hkE7Gl78_5uex1yI,1870
|
|
8
|
+
ipspot-0.5.dist-info/licenses/AUTHORS.md,sha256=5ZvxP1KnuVkurB4psQDSqnqiAyn44q1aeiXUsnYgps4,411
|
|
9
|
+
ipspot-0.5.dist-info/licenses/LICENSE,sha256=0aOd4wzZRoSH_35UZXRHS7alPFTtuFEBJrajLuonEIw,1067
|
|
10
|
+
ipspot-0.5.dist-info/METADATA,sha256=Sbzs3OQ0QZH22X8bNrcLxWbYb1zKBtKwbPf9AVAEon0,13824
|
|
11
|
+
ipspot-0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
ipspot-0.5.dist-info/entry_points.txt,sha256=DJVLepYr8H3UcvWekU5Jy-tcr_xmWrphzgWGNOd3hlk,43
|
|
13
|
+
ipspot-0.5.dist-info/top_level.txt,sha256=v0WgE1z2iCy_bXU53fVcllwHLTvGNBIvq8u3KPC2_Sc,7
|
|
14
|
+
ipspot-0.5.dist-info/RECORD,,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Core Developers
|
|
2
2
|
----------
|
|
3
3
|
- Sepand Haghighi - Open Science Laboratory ([Github](https://github.com/sepandhaghighi)) **
|
|
4
|
+
- Sadra Sabouri - Open Science Laboratory ([Github](https://github.com/sadrasabouri))
|
|
5
|
+
- AmirHosein Rostami - Open Science Laboratory ([Github](https://github.com/AHReccese))
|
|
4
6
|
|
|
5
7
|
** **Maintainer**
|
|
6
8
|
|