Files
FunguyBot/plugins/geo.py
T

123 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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"<strong>Country:</strong> {country} ({country_code})<br>"
f"<strong>Region:</strong> {region}<br>"
f"<strong>City:</strong> {city}<br>"
f"<strong>Postal Code:</strong> {postal}<br>"
f"<strong>Coordinates:</strong> {latitude}, {longitude}<br>"
f"<strong>Timezone:</strong> {timezone}<br>"
f"<strong>ISP/Organization:</strong> {isp}<br>"
f"<strong>ASN:</strong> {asn}<br>")
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 <ip/domain>")
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__ = """<details><summary><strong>!geo</strong> IP / domain geolocation</summary>
<ul><li><code>!geo &lt;ip&gt;</code> or <code>!geo &lt;domain&gt;</code></li></ul></details>"""