Skip to content

Commit 76a1e79

Browse files
authored
Add support for db-ip.com (#99)
* `CHANGELOG.md` updated * `README.md` updated * `_db_ip_com_ipv4` implemented * `DB_IP_COM` added to `IPv4API` * add test for ipv4 for `db-ip.com` * apply `autopep8.sh` [current implementation] * update to force ipv4 * add additional tests + unify spacing between test functions
1 parent d1fd333 commit 76a1e79

7 files changed

Lines changed: 62 additions & 58 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99
- Support [wtfismyip.com](https://wtfismyip.com/json) IPv6 API
1010
- Support [myip.wtf](https://myip.wtf/) IPv6 API
1111
- Support [myip.wtf](https://myip.wtf/) IPv4 API
12+
- Support [db-ip.com](https://api.db-ip.com/v2/free/self) IPv4 API
1213
### Changed
1314
- `README.md` updated
1415
## [0.7] - 2025-12-09

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ Public IP and Location Info:
187187

188188
#### IPv4 API
189189

190-
ℹ️ `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`]
190+
ℹ️ `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`]
191191

192192
ℹ️ The default value: `auto-safe`
193193

ipspot/ipv4.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ def _wtfismyip_com_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
467467

468468

469469
def _myip_wtf_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
470-
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
470+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
471471
"""
472472
Get public IP and geolocation using myip.wtf.
473473
@@ -494,6 +494,34 @@ def _myip_wtf_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
494494
return {"status": False, "error": str(e)}
495495

496496

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+
497525
IPV4_API_MAP = {
498526
IPv4API.IFCONFIG_CO: {
499527
"thread_safe": False,
@@ -575,11 +603,16 @@ def _myip_wtf_ipv4(geo: bool, timeout: Union[float, Tuple[float, float]]
575603
"geo": True,
576604
"function": _myip_wtf_ipv4
577605
},
606+
IPv4API.DB_IP_COM: {
607+
"thread_safe": False,
608+
"geo": True,
609+
"function": _db_ip_com_ipv4
610+
},
578611
}
579612

580613

581-
def get_public_ipv4(api: IPv4API=IPv4API.AUTO_SAFE, geo: bool=False,
582-
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,
583616
max_retries: int = 0,
584617
retry_delay: float = 1.0,
585618
backoff_factor: float = 1.0) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:

ipspot/ipv6.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ def _wtfismyip_com_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
321321

322322

323323
def _myip_wtf_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
324-
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
324+
) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
325325
"""
326326
Get public IP and geolocation using myip.wtf.
327327
@@ -407,8 +407,8 @@ def _myip_wtf_ipv6(geo: bool, timeout: Union[float, Tuple[float, float]]
407407
}
408408

409409

410-
def get_public_ipv6(api: IPv6API=IPv6API.AUTO_SAFE, geo: bool=False,
411-
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,
412412
max_retries: int = 0,
413413
retry_delay: float = 1.0,
414414
backoff_factor: float = 1.0) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:

ipspot/params.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class IPv4API(Enum):
3939
IPWHO_IS = "ipwho.is"
4040
WTFISMYIP_COM = "wtfismyip.com"
4141
MYIP_WTF = "myip.wtf"
42+
DB_IP_COM = "db-ip.com"
4243

4344

4445
class IPv6API(Enum):

tests/test_ipv4_api.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,11 @@ def test_public_ipv4_myip_wtf_success():
145145
assert is_ipv4(result["data"]["ip"])
146146
assert set(result["data"].keys()) == DATA_ITEMS
147147
assert result["data"]["api"] == "myip.wtf"
148+
149+
150+
def test_public_ipv4_db_ip_com_success():
151+
result = get_public_ipv4(api=IPv4API.DB_IP_COM, geo=True, timeout=40, max_retries=4, retry_delay=90, backoff_factor=1.1)
152+
assert result["status"]
153+
assert is_ipv4(result["data"]["ip"])
154+
assert set(result["data"].keys()) == DATA_ITEMS
155+
assert result["data"]["api"] == "db-ip.com"

tests/test_ipv4_functions.py

Lines changed: 12 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,6 @@ def test_get_private_ipv4_exception():
7575
assert result["error"] == "Test error"
7676

7777

78-
79-
80-
8178
def test_public_ipv4_auto_timeout_error():
8279
result = get_public_ipv4(api=IPv4API.AUTO, geo=True, timeout="5")
8380
assert not result["status"]
@@ -90,9 +87,6 @@ def test_public_ipv4_auto_net_error():
9087
assert result["error"] == "All attempts failed."
9188

9289

93-
94-
95-
9690
def test_public_ipv4_auto_safe_timeout_error():
9791
result = get_public_ipv4(api=IPv4API.AUTO_SAFE, geo=True, timeout="5")
9892
assert not result["status"]
@@ -105,9 +99,6 @@ def test_public_ipv4_auto_safe_net_error():
10599
assert result["error"] == "All attempts failed."
106100

107101

108-
109-
110-
111102
def test_public_ipv4_ipapi_co_timeout_error():
112103
result = get_public_ipv4(api=IPv4API.IPAPI_CO, geo=True, timeout="5")
113104
assert not result["status"]
@@ -120,9 +111,6 @@ def test_public_ipv4_ipapi_co_net_error():
120111
assert result["error"] == "No Internet"
121112

122113

123-
124-
125-
126114
def test_public_ipv4_ipleak_net_timeout_error():
127115
result = get_public_ipv4(api=IPv4API.IPLEAK_NET, geo=True, timeout="5")
128116
assert not result["status"]
@@ -135,9 +123,6 @@ def test_public_ipv4_ipleak_net_net_error():
135123
assert result["error"] == "No Internet"
136124

137125

138-
139-
140-
141126
def test_public_ipv4_my_ip_io_timeout_error():
142127
result = get_public_ipv4(api=IPv4API.MY_IP_IO, geo=True, timeout="5")
143128
assert not result["status"]
@@ -150,9 +135,6 @@ def test_public_ipv4_my_ip_io_net_error():
150135
assert result["error"] == "No Internet"
151136

152137

153-
154-
155-
156138
def test_public_ipv4_ifconfig_co_timeout_error():
157139
result = get_public_ipv4(api=IPv4API.IFCONFIG_CO, geo=True, timeout="5")
158140
assert not result["status"]
@@ -165,9 +147,6 @@ def test_public_ipv4_ifconfig_co_net_error():
165147
assert result["error"] == "No Internet"
166148

167149

168-
169-
170-
171150
def test_public_ipv4_myip_la_timeout_error():
172151
result = get_public_ipv4(api=IPv4API.MYIP_LA, geo=True, timeout="5")
173152
assert not result["status"]
@@ -180,9 +159,6 @@ def test_public_ipv4_myip_la_net_error():
180159
assert result["error"] == "No Internet"
181160

182161

183-
184-
185-
186162
def test_public_ipv4_ipquery_io_timeout_error():
187163
result = get_public_ipv4(api=IPv4API.IPQUERY_IO, geo=True, timeout="5")
188164
assert not result["status"]
@@ -195,9 +171,6 @@ def test_public_ipv4_ipquery_io_net_error():
195171
assert result["error"] == "No Internet"
196172

197173

198-
199-
200-
201174
def test_public_ipv4_ipwho_is_timeout_error():
202175
result = get_public_ipv4(api=IPv4API.IPWHO_IS, geo=True, timeout="5")
203176
assert not result["status"]
@@ -210,9 +183,6 @@ def test_public_ipv4_ipwho_is_net_error():
210183
assert result["error"] == "No Internet"
211184

212185

213-
214-
215-
216186
def test_public_ipv4_freeipapi_com_timeout_error():
217187
result = get_public_ipv4(api=IPv4API.FREEIPAPI_COM, geo=True, timeout="5")
218188
assert not result["status"]
@@ -225,9 +195,6 @@ def test_public_ipv4_freeipapi_com_net_error():
225195
assert result["error"] == "No Internet"
226196

227197

228-
229-
230-
231198
def test_public_ipv4_ip_api_com_timeout_error():
232199
result = get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout="5")
233200
assert not result["status"]
@@ -240,9 +207,6 @@ def test_public_ipv4_ip_api_com_net_error():
240207
assert result["error"] == "No Internet"
241208

242209

243-
244-
245-
246210
def test_public_ipv4_ipinfo_io_timeout_error():
247211
result = get_public_ipv4(api=IPv4API.IPINFO_IO, geo=True, timeout="5")
248212
assert not result["status"]
@@ -255,25 +219,18 @@ def test_public_ipv4_ipinfo_io_net_error():
255219
assert result["error"] == "No Internet"
256220

257221

258-
259-
260-
261222
def test_public_ipv4_ip_sb_timeout_error():
262223
result = get_public_ipv4(api=IPv4API.IP_SB, geo=True, timeout="5")
263224
assert not result["status"]
264225

265226

266-
267227
def test_public_ipv4_ip_sb_net_error():
268228
with mock.patch.object(requests.Session, "get", side_effect=Exception("No Internet")):
269229
result = get_public_ipv4(api=IPv4API.IP_SB)
270230
assert not result["status"]
271231
assert result["error"] == "No Internet"
272232

273233

274-
275-
276-
277234
def test_public_ipv4_ident_me_timeout_error():
278235
result = get_public_ipv4(api=IPv4API.IDENT_ME, geo=True, timeout="5")
279236
assert not result["status"]
@@ -286,9 +243,6 @@ def test_public_ipv4_ident_me_net_error():
286243
assert result["error"] == "No Internet"
287244

288245

289-
290-
291-
292246
def test_public_ipv4_tnedi_me_timeout_error():
293247
result = get_public_ipv4(api=IPv4API.TNEDI_ME, geo=True, timeout="5")
294248
assert not result["status"]
@@ -307,9 +261,6 @@ def test_public_ipv4_api_error():
307261
assert result["error"] == "Unsupported API: api1"
308262

309263

310-
311-
312-
313264
def test_public_ipv4_reallyfreegeoip_org_timeout_error():
314265
result = get_public_ipv4(api=IPv4API.REALLYFREEGEOIP_ORG, geo=True, timeout="5")
315266
assert not result["status"]
@@ -322,8 +273,6 @@ def test_public_ipv4_reallyfreegeoip_org_net_error():
322273
assert result["error"] == "No Internet"
323274

324275

325-
326-
327276
def test_public_ipv4_wtfismyip_com_timeout_error():
328277
result = get_public_ipv4(api=IPv4API.WTFISMYIP_COM, geo=True, timeout="5")
329278
assert not result["status"]
@@ -334,3 +283,15 @@ def test_public_ipv4_wtfismyip_com_net_error():
334283
result = get_public_ipv4(api=IPv4API.WTFISMYIP_COM)
335284
assert not result["status"]
336285
assert result["error"] == "No Internet"
286+
287+
288+
def test_public_ipv4_db_ip_com_timeout_error():
289+
result = get_public_ipv4(api=IPv4API.DB_IP_COM, geo=True, timeout="5")
290+
assert not result["status"]
291+
292+
293+
def test_public_ipv4_db_ip_com_net_error():
294+
with mock.patch.object(requests.Session, "get", side_effect=Exception("No Internet")):
295+
result = get_public_ipv4(api=IPv4API.DB_IP_COM)
296+
assert not result["status"]
297+
assert result["error"] == "No Internet"

0 commit comments

Comments
 (0)