ipspot 0.4__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 CHANGED
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """ipspot modules."""
3
- from .params import IPSPOT_VERSION, IPv4API
3
+ from .params import IPSPOT_VERSION, IPv4API, IPv6API
4
4
  from .ipv4 import get_private_ipv4, get_public_ipv4, is_ipv4
5
+ from .ipv6 import get_private_ipv6, get_public_ipv6, is_ipv6
5
6
  from .utils import is_loopback
6
7
  __version__ = IPSPOT_VERSION
ipspot/cli.py CHANGED
@@ -4,8 +4,9 @@ import argparse
4
4
  from typing import Union, Tuple
5
5
  from art import tprint
6
6
  from .ipv4 import get_public_ipv4, get_private_ipv4
7
+ from .ipv6 import get_public_ipv6, get_private_ipv6
7
8
  from .utils import _filter_parameter
8
- from .params import IPv4API, PARAMETERS_NAME_MAP
9
+ from .params import IPv4API, IPv6API, PARAMETERS_NAME_MAP
9
10
  from .params import IPSPOT_OVERVIEW, IPSPOT_REPO, IPSPOT_VERSION
10
11
 
11
12
 
@@ -17,42 +18,72 @@ def ipspot_info() -> None: # pragma: no cover
17
18
  print("Repo : " + IPSPOT_REPO)
18
19
 
19
20
 
20
- def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE, geo: bool=False,
21
+ def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE,
22
+ ipv6_api: IPv6API = IPv6API.AUTO_SAFE,
23
+ geo: bool=False,
21
24
  timeout: Union[float, Tuple[float, float]]=5,
22
25
  max_retries: int = 0, retry_delay: float = 1.0) -> None: # pragma: no cover
23
26
  """
24
27
  Print collected IP and location data.
25
28
 
26
29
  :param ipv4_api: public IPv4 API
30
+ :param ipv6_api: public IPv6 API
27
31
  :param geo: geolocation flag
28
32
  :param timeout: timeout value for API
29
33
  :param max_retries: number of retries
30
34
  :param retry_delay: delay between retries (in seconds)
31
35
  """
32
- private_result = get_private_ipv4()
33
36
  print("Private IP:\n")
34
- print(" IP: {private_result[data][ip]}".format(private_result=private_result) if private_result["status"]
35
- else " Error: {private_result[error]}".format(private_result=private_result))
37
+ private_ipv4_result = get_private_ipv4()
38
+ if private_ipv4_result["status"]:
39
+ private_ipv4 = private_ipv4_result["data"]["ip"]
40
+ else:
41
+ private_ipv4 = private_ipv4_result["error"]
42
+ print(" IPv4: {private_ipv4}\n".format(private_ipv4=private_ipv4))
43
+
44
+ private_ipv6_result = get_private_ipv6()
45
+ if private_ipv6_result["status"]:
46
+ private_ipv6 = private_ipv6_result["data"]["ip"]
47
+ else:
48
+ private_ipv6 = private_ipv6_result["error"]
49
+ print(" IPv6: {private_ipv6}".format(private_ipv6=private_ipv6))
36
50
 
37
51
  public_title = "\nPublic IP"
38
52
  if geo:
39
53
  public_title += " and Location Info"
40
54
  public_title += ":\n"
41
55
  print(public_title)
42
- public_result = get_public_ipv4(
56
+ print(" IPv4:\n")
57
+ public_ipv4_result = get_public_ipv4(
43
58
  ipv4_api,
44
59
  geo=geo,
45
60
  timeout=timeout,
46
61
  max_retries=max_retries,
47
62
  retry_delay=retry_delay)
48
- if public_result["status"]:
49
- for name, parameter in sorted(public_result["data"].items()):
63
+ if public_ipv4_result["status"]:
64
+ for name, parameter in sorted(public_ipv4_result["data"].items()):
50
65
  print(
51
- " {name}: {parameter}".format(
66
+ " {name}: {parameter}".format(
52
67
  name=PARAMETERS_NAME_MAP[name],
53
68
  parameter=_filter_parameter(parameter)))
54
69
  else:
55
- print(" Error: {public_result[error]}".format(public_result=public_result))
70
+ print(" Error: {public_ipv4_result[error]}".format(public_ipv4_result=public_ipv4_result))
71
+
72
+ print("\n IPv6:\n")
73
+ public_ipv6_result = get_public_ipv6(
74
+ ipv6_api,
75
+ geo=geo,
76
+ timeout=timeout,
77
+ max_retries=max_retries,
78
+ retry_delay=retry_delay)
79
+ if public_ipv6_result["status"]:
80
+ for name, parameter in sorted(public_ipv6_result["data"].items()):
81
+ print(
82
+ " {name}: {parameter}".format(
83
+ name=PARAMETERS_NAME_MAP[name],
84
+ parameter=_filter_parameter(parameter)))
85
+ else:
86
+ print(" Error: {public_ipv6_result[error]}".format(public_ipv6_result=public_ipv6_result))
56
87
 
57
88
 
58
89
  def main() -> None: # pragma: no cover
@@ -65,6 +96,13 @@ def main() -> None: # pragma: no cover
65
96
  choices=[
66
97
  x.value for x in IPv4API],
67
98
  default=IPv4API.AUTO_SAFE.value)
99
+ parser.add_argument(
100
+ '--ipv6-api',
101
+ help='public IPv6 API',
102
+ type=str.lower,
103
+ choices=[
104
+ x.value for x in IPv6API],
105
+ default=IPv6API.AUTO_SAFE.value)
68
106
  parser.add_argument('--info', help='info', nargs="?", const=1)
69
107
  parser.add_argument('--version', help='version', nargs="?", const=1)
70
108
  parser.add_argument('--no-geo', help='no geolocation data', nargs="?", const=1, default=False)
@@ -79,9 +117,11 @@ def main() -> None: # pragma: no cover
79
117
  ipspot_info()
80
118
  else:
81
119
  ipv4_api = IPv4API(args.ipv4_api)
120
+ ipv6_api = IPv6API(args.ipv6_api)
82
121
  geo = not args.no_geo
83
122
  display_ip_info(
84
123
  ipv4_api=ipv4_api,
124
+ ipv6_api=ipv6_api,
85
125
  geo=geo,
86
126
  timeout=args.timeout,
87
127
  max_retries=args.max_retries,
ipspot/ipv4.py CHANGED
@@ -180,7 +180,7 @@ def _my_ip_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
180
180
  return {"status": False, "error": str(e)}
181
181
 
182
182
 
183
- def _ifconfig_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
183
+ def _ifconfig_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]] # very low rate limit
184
184
  =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
185
185
  """
186
186
  Get public IP and geolocation using ifconfig.co.
@@ -436,6 +436,94 @@ def _freeipapi_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, floa
436
436
  return {"status": False, "error": str(e)}
437
437
 
438
438
 
439
+ def _ipquery_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
440
+ ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
441
+ """
442
+ Get public IP and geolocation using ipquery.io.
443
+
444
+ :param geo: geolocation flag
445
+ :param timeout: timeout value for API
446
+ """
447
+ try:
448
+ data = _get_json_ipv4_forced(url="https://api.ipquery.io/?format=json", timeout=timeout)
449
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipquery.io"}}
450
+ if geo:
451
+ loc = data.get("location", {})
452
+ isp = data.get("isp", {})
453
+ geo_data = {
454
+ "city": loc.get("city"),
455
+ "region": loc.get("state"),
456
+ "country": loc.get("country"),
457
+ "country_code": loc.get("country_code"),
458
+ "latitude": loc.get("latitude"),
459
+ "longitude": loc.get("longitude"),
460
+ "timezone": loc.get("timezone"),
461
+ "organization": isp.get("org"),
462
+ }
463
+ result["data"].update(geo_data)
464
+ return result
465
+ except Exception as e:
466
+ return {"status": False, "error": str(e)}
467
+
468
+
469
+ def _ipwho_is_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
470
+ ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
471
+ """
472
+ Get public IP and geolocation using ipwho.is.
473
+
474
+ :param geo: geolocation flag
475
+ :param timeout: timeout value for API
476
+ """
477
+ try:
478
+ data = _get_json_ipv4_forced(url="https://ipwho.is", timeout=timeout)
479
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipwho.is"}}
480
+ if geo:
481
+ connection = data.get("connection", {})
482
+ timezone = data.get("timezone", {})
483
+ geo_data = {
484
+ "city": data.get("city"),
485
+ "region": data.get("region"),
486
+ "country": data.get("country"),
487
+ "country_code": data.get("country_code"),
488
+ "latitude": data.get("latitude"),
489
+ "longitude": data.get("longitude"),
490
+ "organization": connection.get("org"),
491
+ "timezone": timezone.get("id")
492
+ }
493
+ result["data"].update(geo_data)
494
+ return result
495
+ except Exception as e:
496
+ return {"status": False, "error": str(e)}
497
+
498
+
499
+ def _wtfismyip_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
500
+ ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
501
+ """
502
+ Get public IP and geolocation using wtfismyip.com.
503
+
504
+ :param geo: geolocation flag
505
+ :param timeout: timeout value for API
506
+ """
507
+ try:
508
+ data = _get_json_standard(url="https://json.ipv4.wtfismyip.com", timeout=timeout)
509
+ result = {"status": True, "data": {"ip": data["YourFuckingIPAddress"], "api": "wtfismyip.com"}}
510
+ if geo:
511
+ geo_data = {
512
+ "city": data.get("YourFuckingCity"),
513
+ "region": None,
514
+ "country": data.get("YourFuckingCountry"),
515
+ "country_code": data.get("YourFuckingCountryCode"),
516
+ "latitude": None,
517
+ "longitude": None,
518
+ "organization": data.get("YourFuckingISP"),
519
+ "timezone": None
520
+ }
521
+ result["data"].update(geo_data)
522
+ return result
523
+ except Exception as e:
524
+ return {"status": False, "error": str(e)}
525
+
526
+
439
527
  IPV4_API_MAP = {
440
528
  IPv4API.IFCONFIG_CO: {
441
529
  "thread_safe": False,
@@ -497,6 +585,21 @@ IPV4_API_MAP = {
497
585
  "geo": True,
498
586
  "function": _myip_la_ipv4,
499
587
  },
588
+ IPv4API.IPQUERY_IO: {
589
+ "thread_safe": False,
590
+ "geo": True,
591
+ "function": _ipquery_io_ipv4,
592
+ },
593
+ IPv4API.IPWHO_IS: {
594
+ "thread_safe": False,
595
+ "geo": True,
596
+ "function": _ipwho_is_ipv4,
597
+ },
598
+ IPv4API.WTFISMYIP_COM: {
599
+ "thread_safe": True,
600
+ "geo": True,
601
+ "function": _wtfismyip_com_ipv4
602
+ },
500
603
  }
501
604
 
502
605
 
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.4"
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.
@@ -35,6 +35,21 @@ class IPv4API(Enum):
35
35
  REALLYFREEGEOIP_ORG = "reallyfreegeoip.org"
36
36
  MYIP_LA = "myip.la"
37
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"
38
53
 
39
54
 
40
55
  PARAMETERS_NAME_MAP = {
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipspot
3
- Version: 0.4
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.4
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.4](https://github.com/openscilab/ipspot/archive/v0.4.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.4`
111
+ - `pip install ipspot==0.5`
112
112
 
113
113
 
114
114
  ## Usage
@@ -135,6 +135,26 @@ Dynamic: summary
135
135
  {'status': True, 'data': {'ip': '10.36.18.154'}}
136
136
  ```
137
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
+
138
158
  ### CLI
139
159
 
140
160
  ℹ️ You can use `ipspot` or `python -m ipspot` to run this program
@@ -144,7 +164,7 @@ Dynamic: summary
144
164
  ```console
145
165
  > ipspot --version
146
166
 
147
- 0.4
167
+ 0.5
148
168
  ```
149
169
 
150
170
  #### Info
@@ -159,11 +179,11 @@ Dynamic: summary
159
179
  |___||_| |____/ | .__/ \___/ \__|
160
180
  |_|
161
181
 
162
- __ __ ___ _ _
163
- \ \ / / _ / _ \ | || |
164
- \ \ / / (_)| | | | | || |_
165
- \ V / _ | |_| | _ |__ _|
166
- \_/ (_) \___/ (_) |_|
182
+ __ __ ___ ____
183
+ \ \ / / _ / _ \ | ___|
184
+ \ \ / / (_)| | | | |___ \
185
+ \ V / _ | |_| | _ ___) |
186
+ \_/ (_) \___/ (_)|____/
167
187
 
168
188
 
169
189
 
@@ -181,7 +201,9 @@ Repo : https://github.com/openscilab/ipspot
181
201
  > ipspot
182
202
  Private IP:
183
203
 
184
- 10.36.18.154
204
+ IPv4: 192.168.1.35
205
+
206
+ IPv6: fe80::e1bd:f78:b233:21c9
185
207
 
186
208
  Public IP and Location Info:
187
209
 
@@ -199,7 +221,7 @@ Public IP and Location Info:
199
221
 
200
222
  #### IPv4 API
201
223
 
202
- ℹ️ `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`]
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`]
203
225
 
204
226
  ℹ️ The default value: `auto-safe`
205
227
 
@@ -207,20 +229,80 @@ Public IP and Location Info:
207
229
  > ipspot --ipv4-api="ipinfo.io"
208
230
  Private IP:
209
231
 
210
- 10.36.18.154
232
+ IPv4: 192.168.1.35
233
+
234
+ IPv6: fe80::e1bd:f78:b233:21c9
211
235
 
212
236
  Public IP and Location Info:
213
237
 
214
- API: ipinfo.io
215
- City: Leatherhead
216
- Country: N/A
217
- Country Code: GB
218
- IP: xx.xx.xx.xx
219
- Latitude: 51.2965
220
- Longitude: -0.3338
221
- Organization: AS212238 Datacamp Limited
222
- Region: England
223
- 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
224
306
  ```
225
307
 
226
308
  #### No Geolocation
@@ -229,12 +311,21 @@ Public IP and Location Info:
229
311
  > ipspot --no-geo
230
312
  Private IP:
231
313
 
232
- IP: 10.36.18.154
314
+ IPv4: 192.168.1.35
315
+
316
+ IPv6: fe80::5c40:769f:22de:c196
233
317
 
234
318
  Public IP:
235
319
 
236
- API: ipinfo.io
237
- 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
238
329
  ```
239
330
 
240
331
  ## Issues & Bug Reports
@@ -269,6 +360,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
269
360
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
270
361
 
271
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
272
382
  ## [0.4] - 2025-06-09
273
383
  ### Added
274
384
  - Support [ipapi.co](https://ipapi.co/json/)
@@ -328,7 +438,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
328
438
  - `--no-geo` argument
329
439
  - Logo
330
440
 
331
- [Unreleased]: https://github.com/openscilab/ipspot/compare/v0.4...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
332
443
  [0.4]: https://github.com/openscilab/ipspot/compare/v0.3...v0.4
333
444
  [0.3]: https://github.com/openscilab/ipspot/compare/v0.2...v0.3
334
445
  [0.2]: https://github.com/openscilab/ipspot/compare/v0.1...v0.2
@@ -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,13 +0,0 @@
1
- ipspot/__init__.py,sha256=e_1FqqNDD9eoDVw9eKL_1pS7du-Sei3lm6A4UFjYiUo,211
2
- ipspot/__main__.py,sha256=xKHY_tc94SWktkIHV0O9YADAHPZvT8yXOFSm_L2yMno,105
3
- ipspot/cli.py,sha256=M23l9ftqKbdDGhb906q4ywJmcjkHW3VWijyXj76APC4,3222
4
- ipspot/ipv4.py,sha256=zGFvzD2GeM2lFxMWc6lcCphBaj5qY0kxiHE0FBhDoTc,19998
5
- ipspot/params.py,sha256=hdArSL10EEq4vtfxA51ftCkn5WfsAzsxdoX1T11yCdQ,1419
6
- ipspot/utils.py,sha256=YtkyWGljpFc72rSGljsARjlVrF9hkE7Gl78_5uex1yI,1870
7
- ipspot-0.4.dist-info/licenses/AUTHORS.md,sha256=5ZvxP1KnuVkurB4psQDSqnqiAyn44q1aeiXUsnYgps4,411
8
- ipspot-0.4.dist-info/licenses/LICENSE,sha256=0aOd4wzZRoSH_35UZXRHS7alPFTtuFEBJrajLuonEIw,1067
9
- ipspot-0.4.dist-info/METADATA,sha256=8D5MqjjxrCDU60BDPikNoX75iE5bMKVMQX_kFw03WiU,10875
10
- ipspot-0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- ipspot-0.4.dist-info/entry_points.txt,sha256=DJVLepYr8H3UcvWekU5Jy-tcr_xmWrphzgWGNOd3hlk,43
12
- ipspot-0.4.dist-info/top_level.txt,sha256=v0WgE1z2iCy_bXU53fVcllwHLTvGNBIvq8u3KPC2_Sc,7
13
- ipspot-0.4.dist-info/RECORD,,
File without changes