""" This plugin provides IP geolocation functionality using free APIs. """ import logging import aiohttp import simplematrixbotlib as botlib import socket import re from plugins.common import is_public_destination, html_escape, collapsible_summary async def is_valid_ip(ip): try: socket.inet_pton(socket.AF_INET, ip) return True except socket.error: try: socket.inet_pton(socket.AF_INET6, ip) return True except socket.error: return False def is_domain(domain): domain_pattern = re.compile( r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$' ) return bool(domain_pattern.match(domain)) async def resolve_domain(domain): try: return socket.gethostbyname(domain) except socket.gaierror: return None async def query_ip_api_com(ip): url = f"http://ip-api.com/json/{ip}" try: async with aiohttp.ClientSession() as session: async with session.get(url) as response: if response.status == 200: return await response.json() except Exception as e: logging.error(f"ip-api.com error: {e}") return None async def query_ipapi_co(ip): url = f"https://ipapi.co/{ip}/json/" try: async with aiohttp.ClientSession() as session: async with session.get(url) as response: if response.status == 200: return await response.json() except Exception as e: logging.error(f"ipapi.co error: {e}") return None async def query_geolocation(ip): data = await query_ip_api_com(ip) if not data or data.get('status') == 'fail': data = await query_ipapi_co(ip) return data async def format_geolocation_results(ip, data): if not data or ('status' in data and data.get('status') == 'fail'): return f"🔍 No geolocation data found for {ip}." country = data.get('country', 'N/A') country_code = data.get('countryCode', 'N/A') region = data.get('regionName', data.get('region', 'N/A')) city = data.get('city', 'N/A') postal = data.get('zip', 'N/A') latitude = data.get('lat', 'N/A') longitude = data.get('lon', 'N/A') timezone = data.get('timezone', 'N/A') isp = data.get('isp', 'N/A') org = data.get('org', 'N/A') asn = data.get('as', 'N/A') content = (f"Country: {country} ({country_code})
" f"Region: {region}
" f"City: {city}
" f"Postal Code: {postal}
" f"Coordinates: {latitude}, {longitude}
" f"Timezone: {timezone}
" f"ISP/Organization: {isp}
" f"ASN: {asn}
") return collapsible_summary(f"🔍 Geolocation: {ip}", content) async def handle_command(room, message, bot, prefix, config): match = botlib.MessageMatch(room, message, bot, prefix) if match.is_not_from_this_bot() and match.prefix() and match.command("geo"): args = match.args() if len(args) < 1: await bot.api.send_text_message(room.room_id, "Usage: !geo ") return query = args[0].strip() ip = query if is_domain(query): await bot.api.send_text_message(room.room_id, f"🔍 Resolving domain {html_escape(query)}...") ip = await resolve_domain(query) if not ip: await bot.api.send_text_message(room.room_id, f"Failed to resolve {html_escape(query)}.") return if not is_public_destination(ip): await bot.api.send_text_message(room.room_id, "❌ Domain resolves to private IP.") return await bot.api.send_text_message(room.room_id, f"Resolved to {ip}") elif not await is_valid_ip(query): await bot.api.send_text_message(room.room_id, f"Invalid IP/domain: {html_escape(query)}") return else: if not is_public_destination(ip): await bot.api.send_text_message(room.room_id, "❌ Private IP not allowed.") return geo_data = await query_geolocation(ip) result = await format_geolocation_results(ip, geo_data) await bot.api.send_markdown_message(room.room_id, result) __version__ = "1.0.2" __author__ = "Funguy Bot" __description__ = "IP geolocation lookup" __help__ = """
!geo – IP / domain geolocation
"""