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/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.3"
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
- IPAPI = "ipapi"
26
- IPINFO = "ipinfo"
27
- IPSB = "ipsb"
28
- IDENTME = "identme"
29
- TNEDIME = "tnedime"
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
- from typing import Any
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 filter_parameter(parameter: Any) -> Any:
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
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.3
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 :: 2 - Pre-Alpha
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.3](https://github.com/openscilab/ipspot/archive/v0.3.zip) or [Latest Source](https://github.com/openscilab/ipspot/archive/dev.zip)
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.3`
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.IPAPI)
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.IPAPI, geo=True, timeout=10)
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.3
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
- 10.36.18.154
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`, `ipapi`, `ipinfo`, `ipsb`, `identme`, `tnedime`]
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
- 10.36.18.154
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
- API: ipinfo.io
213
- City: Leatherhead
214
- Country: N/A
215
- Country Code: GB
216
- IP: xx.xx.xx.xx
217
- Latitude: 51.2965
218
- Longitude: -0.3338
219
- Organization: AS212238 Datacamp Limited
220
- Region: England
221
- Timezone: Europe/London
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
- IP: 10.36.18.154
314
+ IPv4: 192.168.1.35
315
+
316
+ IPv6: fe80::5c40:769f:22de:c196
231
317
 
232
318
  Public IP:
233
319
 
234
- API: ipinfo.io
235
- IP: xx.xx.xx.xx
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.3...dev
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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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