""" WHOIS lookup plugin – outputs a formatted code block with emojis and aligned columns. """ import logging import whois import ipaddress import re import asyncio import simplematrixbotlib as botlib from plugins.common import collapsible_summary, html_escape, code_block def is_valid_domain(domain): pattern = r'^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$|^[a-zA-Z0-9-]{1,63}$' return re.match(pattern, domain) is not None def is_valid_ip(ip): try: ipaddress.ip_address(ip) return True except ValueError: return False def _build_rows(data): """Build a list of (emoji, label, value) tuples from WHOIS data.""" rows = [] # Domain domain_name = data.domain_name if isinstance(domain_name, list): domain_name = ', '.join(domain_name) rows.append(('🌐', 'Domain', domain_name or 'N/A')) # Registrar / WHOIS Server if data.registrar: rows.append(('🏢', 'Registrar', data.registrar)) if data.whois_server: rows.append(('📡', 'WHOIS Server', data.whois_server)) # Dates creation_date = data.creation_date if creation_date: if isinstance(creation_date, list): creation_date = creation_date[0] rows.append(('📅', 'Created', str(creation_date))) updated_date = data.updated_date if updated_date: if isinstance(updated_date, list): updated_date = updated_date[0] rows.append(('📝', 'Updated', str(updated_date))) expiration_date = data.expiration_date if expiration_date: if isinstance(expiration_date, list): expiration_date = expiration_date[0] rows.append(('⏰', 'Expires', str(expiration_date))) # Name servers if data.name_servers: ns_sorted = sorted(data.name_servers) ns_text = ', '.join(ns_sorted[:5]) if len(ns_sorted) > 5: ns_text += f' (+{len(ns_sorted)-5} more)' rows.append(('🌍', 'Name Servers', ns_text)) # Status if data.status: status = data.status if isinstance(status, list): status = ', '.join(status[:3]) rows.append(('🔒', 'Status', str(status))) # Contact info if data.org: rows.append(('🏛️', 'Organization', data.org)) if data.country: rows.append(('🌍', 'Country', data.country)) if data.state: rows.append(('🏙️', 'State', data.state)) if data.city: rows.append(('🏡', 'City', data.city)) return rows 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("whois"): args = match.args() if len(args) < 1: await bot.api.send_text_message(room.room_id, "Usage: !whois \nExample: !whois example.com") return query = args[0].strip() if not is_valid_domain(query) and not is_valid_ip(query): await bot.api.send_text_message(room.room_id, f"Invalid input: {html_escape(query)}") return await bot.api.send_text_message(room.room_id, f"🔍 Performing WHOIS lookup for {html_escape(query)}...") try: loop = asyncio.get_running_loop() data = await loop.run_in_executor(None, whois.whois, query) rows = _build_rows(data) sections = [{"title": "", "rows": rows}] # no section header block = code_block(f"🌐 WHOIS Report: {html_escape(query)}", sections) output = collapsible_summary(f"🌐 WHOIS Report: {html_escape(query)}", block) await bot.api.send_markdown_message(room.room_id, output) except whois.parser.PywhoisError as e: await bot.api.send_text_message(room.room_id, f"❌ WHOIS lookup failed: {html_escape(str(e))}") except Exception as e: await bot.api.send_text_message(room.room_id, f"❌ Unexpected error: {html_escape(str(e))}") # --------------------------------------------------------------------------- # Plugin Metadata # --------------------------------------------------------------------------- __version__ = "1.2.1" __author__ = "Funguy Bot" __description__ = "Domain WHOIS lookup" __help__ = """
!whois – WHOIS lookup
!whois <domain or IP>   Shows registrar, dates, nameservers, etc. in a clean table.
"""