Coverage for src/couchers/helpers/geoip.py: 24%
41 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
1import logging
2from ipaddress import IPv4Network, IPv6Network
4import geoip2.database
5from geoip2.errors import AddressNotFoundError
7from couchers.config import config
9logger = logging.getLogger(__name__)
12def geoip_approximate_location(ip_address: str) -> str | None:
13 if config["GEOLITE2_CITY_MMDB_FILE_LOCATION"] == "":
14 return None
15 if ip_address is None:
16 return None
17 try:
18 with geoip2.database.Reader(config["GEOLITE2_CITY_MMDB_FILE_LOCATION"]) as reader:
19 response = reader.city(ip_address)
20 city = response.city.name
21 country = response.country.name
22 return f"{city}, {country}" if city else f"{country}"
23 except AddressNotFoundError:
24 return None
25 except Exception as e:
26 logger.error(f"GeoIP failed for {ip_address=}")
27 return None
30def geoip_asn(ip_address: str | None) -> tuple[int, str, IPv4Network | IPv6Network] | None:
31 if config["GEOLITE2_ASN_MMDB_FILE_LOCATION"] == "":
32 return None
33 if ip_address is None:
34 return None
35 try:
36 with geoip2.database.Reader(config["GEOLITE2_ASN_MMDB_FILE_LOCATION"]) as reader:
37 response = reader.asn(ip_address)
38 asn_number = response.autonomous_system_number
39 asn_org = response.autonomous_system_organization
40 network = response.network
41 if asn_number is not None and asn_org is not None and network is not None:
42 return asn_number, asn_org, network
43 raise ValueError(f"Invalid response: {response}")
44 except AddressNotFoundError:
45 return None
46 except Exception as e:
47 logger.error(f"GeoIP failed for {ip_address=}")
48 return None