Source code for pylav.utils.location

from __future__ import annotations

import asyncio
import socket
from math import atan2, cos, radians, sin, sqrt

import aiohttp
from yarl import URL

from pylav.compat import json
from pylav.constants.coordinates import REGION_TO_COUNTRY_COORDINATE_MAPPING


[docs] def distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float: """Calculate the distance between two coordinates on the earth""" dlat = radians(lat2) - radians(lat1) dlon = radians(lon2) - radians(lon1) a = sin(dlat / 2) * sin(dlat / 2) + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon / 2) * sin(dlon / 2) return 6371 * 2 * atan2(sqrt(a), sqrt(1 - a))
[docs] async def closest( data: dict[str, tuple[float, float]], compare_to: tuple[float, float], *, region_pool: set[str] | None = None ) -> tuple[str, tuple[float, float]]: """Get the closest region to the given coordinates""" if region_pool is None: region_pool = set() entries = [(k, v) for k, v in data.items() if not region_pool or k in region_pool] return min( entries, key=lambda p: distance(compare_to[0], compare_to[1], p[1][0], p[1][1]), )
[docs] async def get_closest_region_name_and_coordinate( lat: float, lon: float, region_pool: set[str] | None = None ) -> tuple[str, tuple[float, float]]: """Get the closest region name and coordinate to the given coordinates""" closest_region, closest_coordinates = await closest( REGION_TO_COUNTRY_COORDINATE_MAPPING, compare_to=(lat, lon), region_pool=region_pool ) name, coordinate = next( iter((k, v) for k, v in REGION_TO_COUNTRY_COORDINATE_MAPPING.items() if v == closest_coordinates) ) return name, coordinate
[docs] async def get_coordinates(ip: str | None = None) -> tuple[float, ...]: """Get the coordinates of the given ip address""" url = URL("https://ipinfo.io") if ip: url /= ip url /= "json" async with aiohttp.ClientSession(json_serialize=json.dumps) as session: async with session.get(url) as response: data = await response.json(loads=json.loads) return tuple(map(float, data["loc"].split(",")))
[docs] async def get_closest_discord_region(host: str | None = None) -> tuple[str, tuple[float, float]]: """Get the closest discord region to the given host""" try: if host is None or host in ["localhost", "127.0.0.1", "::1", "0.0.0.0", "::"]: host_ip = None else: host_ip = await asyncio.to_thread(socket.gethostbyname, host) except Exception: # noqa host_ip = None # If there's any issues getting the ip from the hostname, just use the host ip try: latitude, longitude = await get_coordinates(host_ip) return await get_closest_region_name_and_coordinate(lat=latitude, lon=longitude) except Exception: # noqa return "unknown_pylav", (0, 0)