diff --git a/CHANGELOG.md b/CHANGELOG.md index c912b87..b03154d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added - Support [wtfismyip.com](https://wtfismyip.com/json) IPv6 API +- Support [myip.wtf](https://myip.wtf/) IPv6 API +- Support [myip.wtf](https://myip.wtf/) IPv4 API ### Changed - `README.md` updated ## [0.7] - 2025-12-09 diff --git a/README.md b/README.md index 9f3bfc0..c9ba7b5 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ Public IP and Location Info: #### IPv4 API -ℹ️ `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`] +ℹ️ `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`] ℹ️ The default value: `auto-safe` @@ -230,7 +230,7 @@ Public IP and Location Info: #### IPv6 API -ℹ️ `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`] +ℹ️ `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`] ℹ️ The default value: `auto-safe` diff --git a/ipspot/ipv4.py b/ipspot/ipv4.py index 2b8e82b..8ce962d 100644 --- a/ipspot/ipv4.py +++ b/ipspot/ipv4.py @@ -466,6 +466,34 @@ def _wtfismyip_com_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]] return {"status": False, "error": str(e)} +def _myip_wtf_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]] + ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]: + """ + Get public IP and geolocation using myip.wtf. + + :param geo: geolocation flag + :param timeout: timeout value for API + """ + try: + data = _get_json_standard(url="https://json.ipv4.myip.wtf", timeout=timeout) + result = {"status": True, "data": {"ip": data["YourFuckingIPAddress"], "api": "myip.wtf"}} + if geo: + geo_data = { + "city": data.get("YourFuckingCity"), + "region": None, + "country": data.get("YourFuckingCountry"), + "country_code": data.get("YourFuckingCountryCode"), + "latitude": None, + "longitude": None, + "organization": data.get("YourFuckingISP"), + "timezone": None + } + result["data"].update(geo_data) + return result + except Exception as e: + return {"status": False, "error": str(e)} + + IPV4_API_MAP = { IPv4API.IFCONFIG_CO: { "thread_safe": False, @@ -542,6 +570,11 @@ def _wtfismyip_com_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]] "geo": True, "function": _wtfismyip_com_ipv4 }, + IPv4API.MYIP_WTF: { + "thread_safe": True, + "geo": True, + "function": _myip_wtf_ipv4 + }, } diff --git a/ipspot/ipv6.py b/ipspot/ipv6.py index a2ca571..7f8ab4c 100644 --- a/ipspot/ipv6.py +++ b/ipspot/ipv6.py @@ -320,6 +320,34 @@ def _wtfismyip_com_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]] return {"status": False, "error": str(e)} +def _myip_wtf_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]] + ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]: + """ + Get public IP and geolocation using myip.wtf. + + :param geo: geolocation flag + :param timeout: timeout value for API + """ + try: + data = _get_json_standard(url="https://json.ipv6.myip.wtf", timeout=timeout) + result = {"status": True, "data": {"ip": data["YourFuckingIPAddress"], "api": "myip.wtf"}} + if geo: + geo_data = { + "city": data.get("YourFuckingCity"), + "region": None, + "country": data.get("YourFuckingCountry"), + "country_code": data.get("YourFuckingCountryCode"), + "latitude": None, + "longitude": None, + "organization": data.get("YourFuckingISP"), + "timezone": None + } + result["data"].update(geo_data) + return result + except Exception as e: + return {"status": False, "error": str(e)} + + IPV6_API_MAP = { IPv6API.IP_SB: { "thread_safe": True, @@ -371,6 +399,11 @@ def _wtfismyip_com_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]] "geo": True, "function": _wtfismyip_com_ipv6 }, + IPv6API.MYIP_WTF: { + "thread_safe": True, + "geo": True, + "function": _myip_wtf_ipv6 + }, } diff --git a/ipspot/params.py b/ipspot/params.py index 1c4ebf2..0d675e0 100644 --- a/ipspot/params.py +++ b/ipspot/params.py @@ -38,6 +38,7 @@ class IPv4API(Enum): IPQUERY_IO = "ipquery.io" IPWHO_IS = "ipwho.is" WTFISMYIP_COM = "wtfismyip.com" + MYIP_WTF = "myip.wtf" class IPv6API(Enum): @@ -55,6 +56,7 @@ class IPv6API(Enum): MYIP_LA = "myip.la" FREEIPAPI_COM = "freeipapi.com" WTFISMYIP_COM = "wtfismyip.com" + MYIP_WTF = "myip.wtf" PARAMETERS_NAME_MAP = { diff --git a/tests/test_ipv4_api.py b/tests/test_ipv4_api.py index 942ef75..3a47a52 100644 --- a/tests/test_ipv4_api.py +++ b/tests/test_ipv4_api.py @@ -137,3 +137,11 @@ def test_public_ipv4_wtfismyip_com_success(): assert is_ipv4(result["data"]["ip"]) assert set(result["data"].keys()) == DATA_ITEMS assert result["data"]["api"] == "wtfismyip.com" + + +def test_public_ipv4_myip_wtf_success(): + result = get_public_ipv4(api=IPv4API.MYIP_WTF, geo=True, timeout=40, max_retries=4, retry_delay=90, backoff_factor=1.1) + assert result["status"] + assert is_ipv4(result["data"]["ip"]) + assert set(result["data"].keys()) == DATA_ITEMS + assert result["data"]["api"] == "myip.wtf" diff --git a/tests/test_ipv6_api.py b/tests/test_ipv6_api.py index 35d6cf0..0d82723 100644 --- a/tests/test_ipv6_api.py +++ b/tests/test_ipv6_api.py @@ -95,4 +95,12 @@ def test_public_ipv6_auto_safe_success(): result = get_public_ipv6(api=IPv6API.AUTO_SAFE, geo=True, timeout=40, max_retries=4, retry_delay=90, backoff_factor=1.1) assert result["status"] assert is_ipv6(result["data"]["ip"]) - assert set(result["data"].keys()) == DATA_ITEMS \ No newline at end of file + assert set(result["data"].keys()) == DATA_ITEMS + + +def test_public_ipv6_myip_wtf_success(): + result = get_public_ipv6(api=IPv6API.MYIP_WTF, geo=True, timeout=40, max_retries=4, retry_delay=90, backoff_factor=1.1) + assert result["status"] + assert is_ipv6(result["data"]["ip"]) + assert set(result["data"].keys()) == DATA_ITEMS + assert result["data"]["api"] == "myip.wtf" \ No newline at end of file