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/ipv4.py CHANGED
@@ -6,7 +6,7 @@ from typing import Union, Dict, List, Tuple
6
6
  import requests
7
7
  from requests.adapters import HTTPAdapter
8
8
  from urllib3.poolmanager import PoolManager
9
- from .utils import is_loopback
9
+ from .utils import is_loopback, _get_json_standard, _attempt_with_retries
10
10
  from .params import REQUEST_HEADERS, IPv4API
11
11
 
12
12
 
@@ -53,6 +53,21 @@ class IPv4HTTPAdapter(HTTPAdapter):
53
53
  socket.getaddrinfo = self._original_getaddrinfo
54
54
 
55
55
 
56
+ def _get_json_ipv4_forced(url: str, timeout: Union[float, Tuple[float, float]]) -> dict:
57
+ """
58
+ Send GET request with forced IPv4 using IPv4HTTPAdapter that returns JSON response.
59
+
60
+ :param url: API url
61
+ :param timeout: timeout value for API
62
+ """
63
+ with requests.Session() as session:
64
+ session.mount("http://", IPv4HTTPAdapter())
65
+ session.mount("https://", IPv4HTTPAdapter())
66
+ response = session.get(url, headers=REQUEST_HEADERS, timeout=timeout)
67
+ response.raise_for_status()
68
+ return response.json()
69
+
70
+
56
71
  def is_ipv4(ip: str) -> bool:
57
72
  """
58
73
  Check if the given input is a valid IPv4 address.
@@ -81,8 +96,8 @@ def get_private_ipv4() -> Dict[str, Union[bool, Dict[str, str], str]]:
81
96
  return {"status": False, "error": str(e)}
82
97
 
83
98
 
84
- def _ipsb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
85
- =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
99
+ def _ip_sb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
100
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
86
101
  """
87
102
  Get public IP and geolocation using ip.sb.
88
103
 
@@ -90,30 +105,139 @@ def _ipsb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
90
105
  :param timeout: timeout value for API
91
106
  """
92
107
  try:
93
- with requests.Session() as session:
94
- response = session.get("https://api-ipv4.ip.sb/geoip", headers=REQUEST_HEADERS, timeout=timeout)
95
- response.raise_for_status()
96
- data = response.json()
97
- result = {"status": True, "data": {"ip": data.get("ip"), "api": "ip.sb"}}
98
- if geo:
99
- geo_data = {
100
- "city": data.get("city"),
101
- "region": data.get("region"),
102
- "country": data.get("country"),
103
- "country_code": data.get("country_code"),
104
- "latitude": data.get("latitude"),
105
- "longitude": data.get("longitude"),
106
- "organization": data.get("organization"),
107
- "timezone": data.get("timezone")
108
- }
109
- result["data"].update(geo_data)
110
- return result
108
+ data = _get_json_standard(url="https://api-ipv4.ip.sb/geoip", timeout=timeout)
109
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ip.sb"}}
110
+ if geo:
111
+ geo_data = {
112
+ "city": data.get("city"),
113
+ "region": data.get("region"),
114
+ "country": data.get("country"),
115
+ "country_code": data.get("country_code"),
116
+ "latitude": data.get("latitude"),
117
+ "longitude": data.get("longitude"),
118
+ "organization": data.get("organization"),
119
+ "timezone": data.get("timezone")
120
+ }
121
+ result["data"].update(geo_data)
122
+ return result
111
123
  except Exception as e:
112
124
  return {"status": False, "error": str(e)}
113
125
 
114
126
 
115
- def _ipapi_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
116
- =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
127
+ def _ipleak_net_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
128
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
129
+ """
130
+ Get public IP and geolocation using ipleak.net.
131
+
132
+ :param geo: geolocation flag
133
+ :param timeout: timeout value for API
134
+ """
135
+ try:
136
+ data = _get_json_standard(url="https://ipv4.ipleak.net/json/", timeout=timeout)
137
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipleak.net"}}
138
+ if geo:
139
+ geo_data = {
140
+ "city": data.get("city_name"),
141
+ "region": data.get("region_name"),
142
+ "country": data.get("country_name"),
143
+ "country_code": data.get("country_code"),
144
+ "latitude": data.get("latitude"),
145
+ "longitude": data.get("longitude"),
146
+ "organization": data.get("isp_name"),
147
+ "timezone": data.get("time_zone")
148
+ }
149
+ result["data"].update(geo_data)
150
+ return result
151
+ except Exception as e:
152
+ return {"status": False, "error": str(e)}
153
+
154
+
155
+ def _my_ip_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
156
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
157
+ """
158
+ Get public IP and geolocation using my-ip.io.
159
+
160
+ :param geo: geolocation flag
161
+ :param timeout: timeout value for API
162
+ """
163
+ try:
164
+ data = _get_json_standard(url="https://api4.my-ip.io/v2/ip.json", timeout=timeout)
165
+ result = {"status": True, "data": {"ip": data["ip"], "api": "my-ip.io"}}
166
+ if geo:
167
+ geo_data = {
168
+ "city": data.get("city"),
169
+ "region": data.get("region"),
170
+ "country": data.get("country", {}).get("name"),
171
+ "country_code": data.get("country", {}).get("code"),
172
+ "latitude": data.get("location", {}).get("lat"),
173
+ "longitude": data.get("location", {}).get("lon"),
174
+ "organization": data.get("asn", {}).get("name"),
175
+ "timezone": data.get("timeZone")
176
+ }
177
+ result["data"].update(geo_data)
178
+ return result
179
+ except Exception as e:
180
+ return {"status": False, "error": str(e)}
181
+
182
+
183
+ def _ifconfig_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]] # very low rate limit
184
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
185
+ """
186
+ Get public IP and geolocation using ifconfig.co.
187
+
188
+ :param geo: geolocation flag
189
+ :param timeout: timeout value for API
190
+ """
191
+ try:
192
+ data = _get_json_ipv4_forced(url="https://ifconfig.co/json", timeout=timeout)
193
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ifconfig.co"}}
194
+ if geo:
195
+ geo_data = {
196
+ "city": data.get("city"),
197
+ "region": data.get("region_name"),
198
+ "country": data.get("country"),
199
+ "country_code": data.get("country_iso"),
200
+ "latitude": data.get("latitude"),
201
+ "longitude": data.get("longitude"),
202
+ "organization": data.get("asn_org"),
203
+ "timezone": data.get("time_zone")
204
+ }
205
+ result["data"].update(geo_data)
206
+ return result
207
+ except Exception as e:
208
+ return {"status": False, "error": str(e)}
209
+
210
+
211
+ def _ipapi_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
212
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
213
+ """
214
+ Get public IP and geolocation using ipapi.co.
215
+
216
+ :param geo: geolocation flag
217
+ :param timeout: timeout value for API
218
+ """
219
+ try:
220
+ data = _get_json_ipv4_forced(url="https://ipapi.co/json/", timeout=timeout)
221
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipapi.co"}}
222
+ if geo:
223
+ geo_data = {
224
+ "city": data.get("city"),
225
+ "region": data.get("region"),
226
+ "country": data.get("country_name"),
227
+ "country_code": data.get("country_code"),
228
+ "latitude": data.get("latitude"),
229
+ "longitude": data.get("longitude"),
230
+ "organization": data.get("org"),
231
+ "timezone": data.get("timezone")
232
+ }
233
+ result["data"].update(geo_data)
234
+ return result
235
+ except Exception as e:
236
+ return {"status": False, "error": str(e)}
237
+
238
+
239
+ def _ip_api_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
240
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
117
241
  """
118
242
  Get public IP and geolocation using ip-api.com.
119
243
 
@@ -121,34 +245,29 @@ def _ipapi_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
121
245
  :param timeout: timeout value for API
122
246
  """
123
247
  try:
124
- with requests.Session() as session:
125
- session.mount("http://", IPv4HTTPAdapter())
126
- session.mount("https://", IPv4HTTPAdapter())
127
- response = session.get("http://ip-api.com/json/", headers=REQUEST_HEADERS, timeout=timeout)
128
- response.raise_for_status()
129
- data = response.json()
130
- if data.get("status") != "success":
131
- return {"status": False, "error": "ip-api lookup failed"}
132
- result = {"status": True, "data": {"ip": data.get("query"), "api": "ip-api.com"}}
133
- if geo:
134
- geo_data = {
135
- "city": data.get("city"),
136
- "region": data.get("regionName"),
137
- "country": data.get("country"),
138
- "country_code": data.get("countryCode"),
139
- "latitude": data.get("lat"),
140
- "longitude": data.get("lon"),
141
- "organization": data.get("org"),
142
- "timezone": data.get("timezone")
143
- }
144
- result["data"].update(geo_data)
145
- return result
248
+ data = _get_json_ipv4_forced(url="http://ip-api.com/json/", timeout=timeout)
249
+ if data.get("status") != "success":
250
+ return {"status": False, "error": "ip-api lookup failed"}
251
+ result = {"status": True, "data": {"ip": data["query"], "api": "ip-api.com"}}
252
+ if geo:
253
+ geo_data = {
254
+ "city": data.get("city"),
255
+ "region": data.get("regionName"),
256
+ "country": data.get("country"),
257
+ "country_code": data.get("countryCode"),
258
+ "latitude": data.get("lat"),
259
+ "longitude": data.get("lon"),
260
+ "organization": data.get("org"),
261
+ "timezone": data.get("timezone")
262
+ }
263
+ result["data"].update(geo_data)
264
+ return result
146
265
  except Exception as e:
147
266
  return {"status": False, "error": str(e)}
148
267
 
149
268
 
150
- def _ipinfo_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
151
- =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
269
+ def _ipinfo_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
270
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
152
271
  """
153
272
  Get public IP and geolocation using ipinfo.io.
154
273
 
@@ -156,27 +275,50 @@ def _ipinfo_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
156
275
  :param timeout: timeout value for API
157
276
  """
158
277
  try:
159
- with requests.Session() as session:
160
- session.mount("http://", IPv4HTTPAdapter())
161
- session.mount("https://", IPv4HTTPAdapter())
162
- response = session.get("https://ipinfo.io/json", headers=REQUEST_HEADERS, timeout=timeout)
163
- response.raise_for_status()
164
- data = response.json()
165
- result = {"status": True, "data": {"ip": data.get("ip"), "api": "ipinfo.io"}}
166
- if geo:
167
- loc = data.get("loc", "").split(",")
168
- geo_data = {
169
- "city": data.get("city"),
170
- "region": data.get("region"),
171
- "country": None,
172
- "country_code": data.get("country"),
173
- "latitude": float(loc[0]) if len(loc) == 2 else None,
174
- "longitude": float(loc[1]) if len(loc) == 2 else None,
175
- "organization": data.get("org"),
176
- "timezone": data.get("timezone")
177
- }
178
- result["data"].update(geo_data)
179
- return result
278
+ data = _get_json_ipv4_forced(url="https://ipinfo.io/json", timeout=timeout)
279
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipinfo.io"}}
280
+ if geo:
281
+ loc = data.get("loc", "").split(",")
282
+ geo_data = {
283
+ "city": data.get("city"),
284
+ "region": data.get("region"),
285
+ "country": None,
286
+ "country_code": data.get("country"),
287
+ "latitude": float(loc[0]) if len(loc) == 2 else None,
288
+ "longitude": float(loc[1]) if len(loc) == 2 else None,
289
+ "organization": data.get("org"),
290
+ "timezone": data.get("timezone")
291
+ }
292
+ result["data"].update(geo_data)
293
+ return result
294
+ except Exception as e:
295
+ return {"status": False, "error": str(e)}
296
+
297
+
298
+ def _reallyfreegeoip_org_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
299
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
300
+ """
301
+ Get public IP and geolocation using reallyfreegeoip.org.
302
+
303
+ :param geo: geolocation flag
304
+ :param timeout: timeout value for API
305
+ """
306
+ try:
307
+ data = _get_json_ipv4_forced(url="https://reallyfreegeoip.org/json/", timeout=timeout)
308
+ result = {"status": True, "data": {"ip": data["ip"], "api": "reallyfreegeoip.org"}}
309
+ if geo:
310
+ geo_data = {
311
+ "city": data.get("city"),
312
+ "region": data.get("region_name"),
313
+ "country": data.get("country_name"),
314
+ "country_code": data.get("country_code"),
315
+ "latitude": data.get("latitude"),
316
+ "longitude": data.get("longitude"),
317
+ "organization": None, # does not provide organization
318
+ "timezone": data.get("time_zone")
319
+ }
320
+ result["data"].update(geo_data)
321
+ return result
180
322
  except Exception as e:
181
323
  return {"status": False, "error": str(e)}
182
324
 
@@ -190,30 +332,27 @@ def _ident_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
190
332
  :param timeout: timeout value for API
191
333
  """
192
334
  try:
193
- with requests.Session() as session:
194
- response = session.get("https://4.ident.me/json", headers=REQUEST_HEADERS, timeout=timeout)
195
- response.raise_for_status()
196
- data = response.json()
197
- result = {"status": True, "data": {"ip": data.get("ip"), "api": "ident.me"}}
198
- if geo:
199
- geo_data = {
200
- "city": data.get("city"),
201
- "region": None,
202
- "country": data.get("country"),
203
- "country_code": data.get("cc"),
204
- "latitude": data.get("latitude"),
205
- "longitude": data.get("longitude"),
206
- "organization": data.get("aso"),
207
- "timezone": data.get("tz")
208
- }
209
- result["data"].update(geo_data)
210
- return result
335
+ data = _get_json_standard(url="https://4.ident.me/json", timeout=timeout)
336
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ident.me"}}
337
+ if geo:
338
+ geo_data = {
339
+ "city": data.get("city"),
340
+ "region": None,
341
+ "country": data.get("country"),
342
+ "country_code": data.get("cc"),
343
+ "latitude": data.get("latitude"),
344
+ "longitude": data.get("longitude"),
345
+ "organization": data.get("aso"),
346
+ "timezone": data.get("tz")
347
+ }
348
+ result["data"].update(geo_data)
349
+ return result
211
350
  except Exception as e:
212
351
  return {"status": False, "error": str(e)}
213
352
 
214
353
 
215
- def _tnedime_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
216
- =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
354
+ def _tnedi_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
355
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
217
356
  """
218
357
  Get public IP and geolocation using tnedi.me.
219
358
 
@@ -221,53 +360,284 @@ def _tnedime_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
221
360
  :param timeout: timeout value for API
222
361
  """
223
362
  try:
224
- with requests.Session() as session:
225
- response = session.get("https://4.tnedi.me/json", headers=REQUEST_HEADERS, timeout=timeout)
226
- response.raise_for_status()
227
- data = response.json()
228
- result = {"status": True, "data": {"ip": data.get("ip"), "api": "tnedi.me"}}
229
- if geo:
230
- geo_data = {
231
- "city": data.get("city"),
232
- "region": None,
233
- "country": data.get("country"),
234
- "country_code": data.get("cc"),
235
- "latitude": data.get("latitude"),
236
- "longitude": data.get("longitude"),
237
- "organization": data.get("aso"),
238
- "timezone": data.get("tz")
239
- }
240
- result["data"].update(geo_data)
241
- return result
363
+ data = _get_json_standard(url="https://4.tnedi.me/json", timeout=timeout)
364
+ result = {"status": True, "data": {"ip": data["ip"], "api": "tnedi.me"}}
365
+ if geo:
366
+ geo_data = {
367
+ "city": data.get("city"),
368
+ "region": None,
369
+ "country": data.get("country"),
370
+ "country_code": data.get("cc"),
371
+ "latitude": data.get("latitude"),
372
+ "longitude": data.get("longitude"),
373
+ "organization": data.get("aso"),
374
+ "timezone": data.get("tz")
375
+ }
376
+ result["data"].update(geo_data)
377
+ return result
378
+ except Exception as e:
379
+ return {"status": False, "error": str(e)}
380
+
381
+
382
+ def _myip_la_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
383
+ ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
384
+ """
385
+ Get public IP and geolocation using myip.la.
386
+
387
+ :param geo: geolocation flag
388
+ :param timeout: timeout value for API
389
+ """
390
+ try:
391
+ data = _get_json_ipv4_forced(url="https://api.myip.la/en?json", timeout=timeout)
392
+ result = {"status": True, "data": {"ip": data["ip"], "api": "myip.la"}}
393
+ if geo:
394
+ loc = data.get("location", {})
395
+ geo_data = {
396
+ "city": loc.get("city"),
397
+ "region": loc.get("province"),
398
+ "country": loc.get("country_name"),
399
+ "country_code": loc.get("country_code"),
400
+ "latitude": float(loc.get("latitude")) if loc.get("latitude") else None,
401
+ "longitude": float(loc.get("longitude")) if loc.get("longitude") else None,
402
+ "organization": None,
403
+ "timezone": None
404
+ }
405
+ result["data"].update(geo_data)
406
+ return result
407
+ except Exception as e:
408
+ return {"status": False, "error": str(e)}
409
+
410
+
411
+ def _freeipapi_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
412
+ ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
413
+ """
414
+ Get public IP and geolocation using freeipapi.com.
415
+
416
+ :param geo: geolocation flag
417
+ :param timeout: timeout value for API
418
+ """
419
+ try:
420
+ data = _get_json_ipv4_forced(url="https://freeipapi.com/api/json", timeout=timeout)
421
+ result = {"status": True, "data": {"ip": data["ipAddress"], "api": "freeipapi.com"}}
422
+ if geo:
423
+ geo_data = {
424
+ "city": data.get("cityName"),
425
+ "region": data.get("regionName"),
426
+ "country": data.get("countryName"),
427
+ "country_code": data.get("countryCode"),
428
+ "latitude": data.get("latitude"),
429
+ "longitude": data.get("longitude"),
430
+ "organization": None,
431
+ "timezone": data.get("timeZone")
432
+ }
433
+ result["data"].update(geo_data)
434
+ return result
435
+ except Exception as e:
436
+ return {"status": False, "error": str(e)}
437
+
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
242
523
  except Exception as e:
243
524
  return {"status": False, "error": str(e)}
244
525
 
245
526
 
246
- def get_public_ipv4(api: IPv4API=IPv4API.AUTO, geo: bool=False,
247
- timeout: Union[float, Tuple[float, float]]=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
527
+ IPV4_API_MAP = {
528
+ IPv4API.IFCONFIG_CO: {
529
+ "thread_safe": False,
530
+ "geo": True,
531
+ "function": _ifconfig_co_ipv4
532
+ },
533
+ IPv4API.IDENT_ME: {
534
+ "thread_safe": True,
535
+ "geo": True,
536
+ "function": _ident_me_ipv4
537
+ },
538
+ IPv4API.TNEDI_ME: {
539
+ "thread_safe": True,
540
+ "geo": True,
541
+ "function": _tnedi_me_ipv4
542
+ },
543
+ IPv4API.IP_SB: {
544
+ "thread_safe": True,
545
+ "geo": True,
546
+ "function": _ip_sb_ipv4
547
+ },
548
+ IPv4API.IPLEAK_NET: {
549
+ "thread_safe": True,
550
+ "geo": True,
551
+ "function": _ipleak_net_ipv4
552
+ },
553
+ IPv4API.MY_IP_IO: {
554
+ "thread_safe": True,
555
+ "geo": True,
556
+ "function": _my_ip_io_ipv4
557
+ },
558
+ IPv4API.IP_API_COM: {
559
+ "thread_safe": False,
560
+ "geo": True,
561
+ "function": _ip_api_com_ipv4
562
+ },
563
+ IPv4API.IPINFO_IO: {
564
+ "thread_safe": False,
565
+ "geo": True,
566
+ "function": _ipinfo_io_ipv4
567
+ },
568
+ IPv4API.IPAPI_CO: {
569
+ "thread_safe": False,
570
+ "geo": True,
571
+ "function": _ipapi_co_ipv4
572
+ },
573
+ IPv4API.REALLYFREEGEOIP_ORG: {
574
+ "thread_safe": False,
575
+ "geo": True,
576
+ "function": _reallyfreegeoip_org_ipv4
577
+ },
578
+ IPv4API.FREEIPAPI_COM: {
579
+ "thread_safe": False,
580
+ "geo": True,
581
+ "function": _freeipapi_com_ipv4,
582
+ },
583
+ IPv4API.MYIP_LA: {
584
+ "thread_safe": False,
585
+ "geo": True,
586
+ "function": _myip_la_ipv4,
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
+ },
603
+ }
604
+
605
+
606
+ def get_public_ipv4(api: IPv4API=IPv4API.AUTO_SAFE, geo: bool=False,
607
+ timeout: Union[float, Tuple[float, float]]=5,
608
+ max_retries: int = 0,
609
+ retry_delay: float = 1.0) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
248
610
  """
249
611
  Get public IPv4 and geolocation info based on the selected API.
250
612
 
251
613
  :param api: public IPv4 API
252
614
  :param geo: geolocation flag
253
615
  :param timeout: timeout value for API
616
+ :param max_retries: number of retries
617
+ :param retry_delay: delay between retries (in seconds)
254
618
  """
255
- api_map = {
256
- IPv4API.IDENTME: _ident_me_ipv4,
257
- IPv4API.TNEDIME: _tnedime_ipv4,
258
- IPv4API.IPSB: _ipsb_ipv4,
259
- IPv4API.IPAPI: _ipapi_ipv4,
260
- IPv4API.IPINFO: _ipinfo_ipv4,
261
- }
262
-
263
- if api == IPv4API.AUTO:
264
- for _, func in api_map.items():
265
- result = func(geo=geo, timeout=timeout)
619
+ if api in [IPv4API.AUTO, IPv4API.AUTO_SAFE]:
620
+ for _, api_data in IPV4_API_MAP.items():
621
+ if api == IPv4API.AUTO_SAFE and not api_data["thread_safe"]:
622
+ continue
623
+ func = api_data["function"]
624
+ result = _attempt_with_retries(
625
+ func=func,
626
+ max_retries=max_retries,
627
+ retry_delay=retry_delay,
628
+ geo=geo,
629
+ timeout=timeout)
266
630
  if result["status"]:
267
631
  return result
268
632
  return {"status": False, "error": "All attempts failed."}
269
633
  else:
270
- func = api_map.get(api)
271
- if func:
272
- return func(geo=geo, timeout=timeout)
634
+ api_data = IPV4_API_MAP.get(api)
635
+ if api_data:
636
+ func = api_data["function"]
637
+ return _attempt_with_retries(
638
+ func=func,
639
+ max_retries=max_retries,
640
+ retry_delay=retry_delay,
641
+ geo=geo,
642
+ timeout=timeout)
273
643
  return {"status": False, "error": "Unsupported API: {api}".format(api=api)}