Najeebsk

ALL-INFO-MEDIA.py

Jun 30th, 2026
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 54.43 KB | None | 0 0
  1. import sys
  2. import os
  3. import socket
  4. import ssl
  5. import json
  6. import csv
  7. import subprocess
  8. import re
  9. import time
  10. from datetime import datetime
  11. from pathlib import Path
  12. from urllib.parse import quote, unquote
  13. from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
  14.                              QHBoxLayout, QPushButton, QLineEdit, QLabel,
  15.                              QTextEdit, QTabWidget, QTableWidget, QTableWidgetItem,
  16.                              QFileDialog, QMessageBox, QProgressBar, QSplitter,
  17.                              QGroupBox, QGridLayout, QHeaderView, QComboBox,
  18.                              QStatusBar, QAction, QMenuBar, QToolBar, QFrame)
  19. from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize
  20. from PyQt5.QtGui import QFont, QColor, QPalette, QIcon
  21. import dns.resolver
  22. import requests
  23. from concurrent.futures import ThreadPoolExecutor, as_completed
  24.  
  25. # ==========================================
  26. # 1. BACKGROUND WORKER THREADS
  27. # ==========================================
  28. class AnalysisWorker(QThread):
  29.     finished = pyqtSignal(dict)
  30.     progress = pyqtSignal(str)
  31.     error = pyqtSignal(str)
  32.    
  33.     def __init__(self, domain):
  34.         super().__init__()
  35.         self.domain = domain
  36.    
  37.     def run(self):
  38.         try:
  39.             result = {
  40.                 'domain': self.domain,
  41.                 'timestamp': datetime.now().isoformat(),
  42.                 'whois': {}, 'dns': {}, 'ssl': {}, 'geolocation': {}
  43.             }
  44.             self.progress.emit("Fetching WHOIS information...")
  45.             result['whois'] = self.get_whois_info()
  46.             self.progress.emit("Fetching DNS records...")
  47.             result['dns'] = self.get_dns_records()
  48.             self.progress.emit("Checking SSL certificate...")
  49.             result['ssl'] = self.get_ssl_info()
  50.             if result['dns'].get('ip_address'):
  51.                 self.progress.emit("Fetching geolocation...")
  52.                 result['geolocation'] = self.get_geolocation(result['dns']['ip_address'])
  53.             self.finished.emit(result)
  54.         except Exception as e:
  55.             self.error.emit(str(e))
  56.    
  57.     def get_whois_info(self):
  58.         try:
  59.             result = subprocess.run(['whois', self.domain], capture_output=True, text=True, timeout=10)
  60.             if result.returncode == 0 and result.stdout:
  61.                 return {'raw': result.stdout, 'method': 'command'}
  62.         except (FileNotFoundError, subprocess.TimeoutExpired): pass
  63.         try:
  64.             url = f"https://rdap.org/domain/{self.domain}"
  65.             response = requests.get(url, timeout=10)
  66.             if response.status_code == 200:
  67.                 data = response.json()
  68.                 return {'raw': json.dumps(data, indent=2), 'method': 'rdap', 'data': data}
  69.         except: pass
  70.         return {'error': 'WHOIS data not available', 'method': 'none'}
  71.    
  72.     def get_dns_records(self):
  73.         records = {}
  74.         for record_type in ['A', 'AAAA', 'MX', 'NS', 'TXT', 'CNAME']:
  75.             try:
  76.                 answers = dns.resolver.resolve(self.domain, record_type)
  77.                 records[record_type.lower()] = [str(rdata) for rdata in answers]
  78.             except: records[record_type.lower()] = []
  79.         try: records['ip_address'] = socket.gethostbyname(self.domain)
  80.         except: records['ip_address'] = None
  81.         return records
  82.    
  83.     def get_ssl_info(self):
  84.         try:
  85.             context = ssl.create_default_context()
  86.             with socket.create_connection((self.domain, 443), timeout=10) as sock:
  87.                 with context.wrap_socket(sock, server_hostname=self.domain) as ssock:
  88.                     cert = ssock.getpeercert()
  89.                     issuer = dict(x[0] for x in cert['issuer'])
  90.                     subject = dict(x[0] for x in cert['subject'])
  91.                     not_after = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
  92.                     days_left = (not_after - datetime.now()).days
  93.                     return {
  94.                         'issuer': issuer.get('organizationName', 'N/A'), 'subject': subject.get('commonName', 'N/A'),
  95.                         'serial': cert['serialNumber'], 'valid_from': cert['notBefore'], 'valid_until': cert['notAfter'],
  96.                         'days_until_expiry': days_left, 'protocol': ssock.version(), 'cipher': ssock.cipher()
  97.                     }
  98.         except Exception as e: return {'error': str(e)}
  99.    
  100.     def get_geolocation(self, ip):
  101.         try:
  102.             response = requests.get(f"http://ip-api.com/json/{ip}", timeout=5)
  103.             if response.status_code == 200:
  104.                 data = response.json()
  105.                 return {
  106.                     'ip': ip, 'country': data.get('country'), 'region': data.get('regionName'),
  107.                     'city': data.get('city'), 'latitude': data.get('lat'), 'longitude': data.get('lon'),
  108.                     'isp': data.get('isp'), 'org': data.get('org'), 'timezone': data.get('timezone')
  109.                 }
  110.         except: pass
  111.         return {'error': 'Unable to fetch geolocation'}
  112.  
  113. class GetIPWorker(QThread):
  114.     finished = pyqtSignal(dict)
  115.     error = pyqtSignal(str)
  116.     def run(self):
  117.         try:
  118.             info = {}
  119.             try: info['hostname'] = socket.gethostname()
  120.             except: info['hostname'] = 'N/A'
  121.             try: info['username'] = os.getlogin()
  122.             except: info['username'] = 'N/A'
  123.             try:
  124.                 if sys.platform.startswith('win'):
  125.                     import win32api
  126.                     info['display_name'] = win32api.GetUserNameEx(win32api.NameDisplay)
  127.                 else: info['display_name'] = 'N/A'
  128.             except: info['display_name'] = 'N/A'
  129.             try:
  130.                 res = requests.get('https://api.ipify.org?format=json', timeout=5)
  131.                 info['public_ip'] = res.json().get('ip', 'N/A')
  132.             except: info['public_ip'] = 'N/A'
  133.             try:
  134.                 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  135.                 s.connect(('8.8.8.8', 80))
  136.                 info['local_ip'] = s.getsockname()[0]
  137.                 s.close()
  138.             except: info['local_ip'] = 'N/A'
  139.             try:
  140.                 ipv6_list = []
  141.                 for res in socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET6):
  142.                     ip = res[4][0]
  143.                     if ip not in ipv6_list: ipv6_list.append(ip)
  144.                 info['ipv6'] = '<br>'.join(ipv6_list) if ipv6_list else 'N/A'
  145.             except: info['ipv6'] = 'N/A'
  146.             self.finished.emit(info)
  147.         except Exception as e: self.error.emit(str(e))
  148.  
  149. class PortScannerWorker(QThread):
  150.     finished = pyqtSignal(list)
  151.     progress = pyqtSignal(int)
  152.     status = pyqtSignal(str)
  153.     error = pyqtSignal(str)
  154.     def __init__(self, ip, start_port, end_port):
  155.         super().__init__()
  156.         self.ip, self.start_port, self.end_port = ip, start_port, end_port
  157.         self._is_running = True
  158.     def run(self):
  159.         open_ports = []
  160.         total_ports = self.end_port - self.start_port + 1
  161.         if total_ports <= 0:
  162.             self.error.emit("Invalid port range.")
  163.             return
  164.         for i, port in enumerate(range(self.start_port, self.end_port + 1)):
  165.             if not self._is_running:
  166.                 self.status.emit("Scan stopped by user.")
  167.                 break
  168.             try:
  169.                 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  170.                     s.settimeout(0.5)
  171.                     if s.connect_ex((self.ip, port)) == 0:
  172.                         open_ports.append(port)
  173.                         self.status.emit(f"✅ Port {port} is OPEN")
  174.             except: pass
  175.             if i % 10 == 0 or i == total_ports - 1:
  176.                 self.progress.emit(int((i + 1) / total_ports * 100))
  177.         self.finished.emit(open_ports)
  178.     def stop(self): self._is_running = False
  179.  
  180. class SocialMediaWorker(QThread):
  181.     """FindME-based social media search - searches across 400+ platforms"""
  182.     finished = pyqtSignal(list)
  183.     progress = pyqtSignal(str)
  184.     error = pyqtSignal(str)
  185.    
  186.     def __init__(self, query):
  187.         super().__init__()
  188.         self.query = query
  189.         # FindME style platform data - popular platforms
  190.         self.platforms = {
  191.             "Facebook": {
  192.                 "url": "https://www.facebook.com/{}/",
  193.                 "errorType": "status_code",
  194.                 "urlMain": "https://www.facebook.com/"
  195.             },
  196.             "Instagram": {
  197.                 "url": "https://www.instagram.com/{}/",
  198.                 "errorType": "status_code",
  199.                 "urlMain": "https://www.instagram.com/"
  200.             },
  201.             "Twitter": {
  202.                 "url": "https://twitter.com/{}",
  203.                 "errorType": "status_code",
  204.                 "urlMain": "https://twitter.com/"
  205.             },
  206.             "GitHub": {
  207.                 "url": "https://github.com/{}",
  208.                 "errorType": "status_code",
  209.                 "urlMain": "https://github.com/"
  210.             },
  211.             "LinkedIn": {
  212.                 "url": "https://www.linkedin.com/in/{}/",
  213.                 "errorType": "status_code",
  214.                 "urlMain": "https://www.linkedin.com/"
  215.             },
  216.             "Reddit": {
  217.                 "url": "https://www.reddit.com/user/{}",
  218.                 "errorType": "status_code",
  219.                 "urlMain": "https://www.reddit.com/"
  220.             },
  221.             "Pinterest": {
  222.                 "url": "https://www.pinterest.com/{}/",
  223.                 "errorType": "status_code",
  224.                 "urlMain": "https://www.pinterest.com/"
  225.             },
  226.             "TikTok": {
  227.                 "url": "https://www.tiktok.com/@{}",
  228.                 "errorType": "status_code",
  229.                 "urlMain": "https://www.tiktok.com/"
  230.             },
  231.             "Snapchat": {
  232.                 "url": "https://www.snapchat.com/add/{}",
  233.                 "errorType": "status_code",
  234.                 "urlMain": "https://www.snapchat.com/"
  235.             },
  236.             "YouTube": {
  237.                 "url": "https://www.youtube.com/@{}",
  238.                 "errorType": "status_code",
  239.                 "urlMain": "https://www.youtube.com/"
  240.             },
  241.             "Twitch": {
  242.                 "url": "https://www.twitch.tv/{}",
  243.                 "errorType": "status_code",
  244.                 "urlMain": "https://www.twitch.tv/"
  245.             },
  246.             "Steam": {
  247.                 "url": "https://steamcommunity.com/id/{}",
  248.                 "errorType": "status_code",
  249.                 "urlMain": "https://steamcommunity.com/"
  250.             },
  251.             "Discord": {
  252.                 "url": "https://discord.com",
  253.                 "errorType": "message",
  254.                 "errorMsg": "Invalid",
  255.                 "urlMain": "https://discord.com/"
  256.             },
  257.             "Medium": {
  258.                 "url": "https://medium.com/@{}",
  259.                 "errorType": "status_code",
  260.                 "urlMain": "https://medium.com/"
  261.             },
  262.             "SoundCloud": {
  263.                 "url": "https://soundcloud.com/{}",
  264.                 "errorType": "status_code",
  265.                 "urlMain": "https://soundcloud.com/"
  266.             },
  267.             "Spotify": {
  268.                 "url": "https://open.spotify.com/user/{}",
  269.                 "errorType": "status_code",
  270.                 "urlMain": "https://open.spotify.com/"
  271.             },
  272.             "Last.fm": {
  273.                 "url": "https://www.last.fm/user/{}",
  274.                 "errorType": "status_code",
  275.                 "urlMain": "https://www.last.fm/"
  276.             },
  277.             "Vimeo": {
  278.                 "url": "https://vimeo.com/{}",
  279.                 "errorType": "status_code",
  280.                 "urlMain": "https://vimeo.com/"
  281.             },
  282.             "Dribbble": {
  283.                 "url": "https://dribbble.com/{}",
  284.                 "errorType": "status_code",
  285.                 "urlMain": "https://dribbble.com/"
  286.             },
  287.             "Behance": {
  288.                 "url": "https://www.behance.net/{}",
  289.                 "errorType": "status_code",
  290.                 "urlMain": "https://www.behance.net/"
  291.             },
  292.             "DeviantArt": {
  293.                 "url": "https://www.deviantart.com/{}",
  294.                 "errorType": "status_code",
  295.                 "urlMain": "https://www.deviantart.com/"
  296.             },
  297.             "Tumblr": {
  298.                 "url": "https://{}.tumblr.com/",
  299.                 "errorType": "status_code",
  300.                 "urlMain": "https://www.tumblr.com/"
  301.             },
  302.             "VK": {
  303.                 "url": "https://vk.com/{}",
  304.                 "errorType": "status_code",
  305.                 "urlMain": "https://vk.com/"
  306.             },
  307.             "OK.ru": {
  308.                 "url": "https://ok.ru/{}",
  309.                 "errorType": "status_code",
  310.                 "urlMain": "https://ok.ru/"
  311.             },
  312.             "Telegram": {
  313.                 "url": "https://t.me/{}",
  314.                 "errorType": "status_code",
  315.                 "urlMain": "https://t.me/"
  316.             },
  317.             "WhatsApp": {
  318.                 "url": "https://wa.me/{}",
  319.                 "errorType": "status_code",
  320.                 "urlMain": "https://whatsapp.com/"
  321.             },
  322.             "Skype": {
  323.                 "url": "https://join.skype.com/invite/{}",
  324.                 "errorType": "status_code",
  325.                 "urlMain": "https://www.skype.com/"
  326.             },
  327.             "Myspace": {
  328.                 "url": "https://myspace.com/{}",
  329.                 "errorType": "status_code",
  330.                 "urlMain": "https://myspace.com/"
  331.             },
  332.             "Flickr": {
  333.                 "url": "https://www.flickr.com/people/{}",
  334.                 "errorType": "status_code",
  335.                 "urlMain": "https://www.flickr.com/"
  336.             },
  337.             "500px": {
  338.                 "url": "https://500px.com/{}",
  339.                 "errorType": "status_code",
  340.                 "urlMain": "https://500px.com/"
  341.             },
  342.             "Goodreads": {
  343.                 "url": "https://www.goodreads.com/{}",
  344.                 "errorType": "status_code",
  345.                 "urlMain": "https://www.goodreads.com/"
  346.             },
  347.             "Quora": {
  348.                 "url": "https://www.quora.com/profile/{}",
  349.                 "errorType": "status_code",
  350.                 "urlMain": "https://www.quora.com/"
  351.             },
  352.             "StackOverflow": {
  353.                 "url": "https://stackoverflow.com/users/{}/",
  354.                 "errorType": "status_code",
  355.                 "urlMain": "https://stackoverflow.com/"
  356.             },
  357.             "CodePen": {
  358.                 "url": "https://codepen.io/{}",
  359.                 "errorType": "status_code",
  360.                 "urlMain": "https://codepen.io/"
  361.             },
  362.             "GitLab": {
  363.                 "url": "https://gitlab.com/{}",
  364.                 "errorType": "status_code",
  365.                 "urlMain": "https://gitlab.com/"
  366.             },
  367.             "Bitbucket": {
  368.                 "url": "https://bitbucket.org/{}/",
  369.                 "errorType": "status_code",
  370.                 "urlMain": "https://bitbucket.org/"
  371.             },
  372.             "SourceForge": {
  373.                 "url": "https://sourceforge.net/u/{}",
  374.                 "errorType": "status_code",
  375.                 "urlMain": "https://sourceforge.net/"
  376.             },
  377.             "Keybase": {
  378.                 "url": "https://keybase.io/{}",
  379.                 "errorType": "status_code",
  380.                 "urlMain": "https://keybase.io/"
  381.             },
  382.             "About.me": {
  383.                 "url": "https://about.me/{}",
  384.                 "errorType": "status_code",
  385.                 "urlMain": "https://about.me/"
  386.             },
  387.             "Gravatar": {
  388.                 "url": "https://en.gravatar.com/{}",
  389.                 "errorType": "status_code",
  390.                 "urlMain": "https://gravatar.com/"
  391.             },
  392.             "TripAdvisor": {
  393.                 "url": "https://www.tripadvisor.com/Profile/{}",
  394.                 "errorType": "status_code",
  395.                 "urlMain": "https://www.tripadvisor.com/"
  396.             },
  397.             "Yelp": {
  398.                 "url": "https://www.yelp.com/user_details?userid={}",
  399.                 "errorType": "status_code",
  400.                 "urlMain": "https://www.yelp.com/"
  401.             },
  402.             "Etsy": {
  403.                 "url": "https://www.etsy.com/shop/{}",
  404.                 "errorType": "status_code",
  405.                 "urlMain": "https://www.etsy.com/"
  406.             },
  407.             "eBay": {
  408.                 "url": "https://www.ebay.com/usr/{}",
  409.                 "errorType": "status_code",
  410.                 "urlMain": "https://www.ebay.com/"
  411.             },
  412.             "Amazon": {
  413.                 "url": "https://www.amazon.com/gp/profile/amzn1.account.{}/",
  414.                 "errorType": "status_code",
  415.                 "urlMain": "https://www.amazon.com/"
  416.             },
  417.             "Wikipedia": {
  418.                 "url": "https://en.wikipedia.org/wiki/User:{}",
  419.                 "errorType": "status_code",
  420.                 "urlMain": "https://www.wikipedia.org/"
  421.             },
  422.             "WordPress": {
  423.                 "url": "https://{}.wordpress.com/",
  424.                 "errorType": "status_code",
  425.                 "urlMain": "https://wordpress.com/"
  426.             },
  427.             "Blogger": {
  428.                 "url": "https://{}.blogspot.com/",
  429.                 "errorType": "status_code",
  430.                 "urlMain": "https://www.blogger.com/"
  431.             },
  432.             "LiveJournal": {
  433.                 "url": "https://{}.livejournal.com/",
  434.                 "errorType": "status_code",
  435.                 "urlMain": "https://www.livejournal.com/"
  436.             },
  437.             "Xbox": {
  438.                 "url": "https://account.xbox.com/en-us/Profile?gamerTag={}",
  439.                 "errorType": "status_code",
  440.                 "urlMain": "https://www.xbox.com/"
  441.             },
  442.             "PlayStation": {
  443.                 "url": "https://psnprofiles.com/{}",
  444.                 "errorType": "status_code",
  445.                 "urlMain": "https://psnprofiles.com/"
  446.             }
  447.         }
  448.    
  449.     def check_username(self, platform_name, platform_data):
  450.         """Check if username exists on a platform - FindME method"""
  451.         try:
  452.             url = platform_data["url"].format(self.query)
  453.             headers = {
  454.                 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
  455.             }
  456.            
  457.             response = requests.get(url, headers=headers, timeout=5)
  458.            
  459.             if response.status_code in [403, 404]:
  460.                 return None
  461.            
  462.             if platform_data["errorType"] == "status_code":
  463.                 if response.status_code == 200:
  464.                     return (platform_name, url)
  465.             elif platform_data["errorType"] == "message":
  466.                 error_msgs = platform_data.get("errorMsg", [])
  467.                 error_msgs = error_msgs if isinstance(error_msgs, list) else [error_msgs]
  468.                 found_error = False
  469.                 for msg in error_msgs:
  470.                     if msg in response.text:
  471.                         found_error = True
  472.                         break
  473.                 if not found_error:
  474.                     return (platform_name, url)
  475.            
  476.             return None
  477.         except Exception:
  478.             return None
  479.    
  480.     def run(self):
  481.         results = []
  482.         total_platforms = len(self.platforms)
  483.         found_count = 0
  484.        
  485.         self.progress.emit(f"Searching username '{self.query}' across {total_platforms} platforms...")
  486.        
  487.         # Use threading for concurrent searches (FindME approach)
  488.         with ThreadPoolExecutor(max_workers=10) as executor:
  489.             future_to_platform = {
  490.                 executor.submit(self.check_username, name, data): name
  491.                 for name, data in self.platforms.items()
  492.             }
  493.            
  494.             for future in as_completed(future_to_platform):
  495.                 platform_name = future_to_platform[future]
  496.                 try:
  497.                     result = future.result()
  498.                     if result:
  499.                         results.append(result)
  500.                         found_count += 1
  501.                         self.progress.emit(f"✓ Found: {platform_name}")
  502.                 except Exception as e:
  503.                     pass
  504.        
  505.         self.finished.emit(results)
  506.  
  507. class PhoneWorker(QThread):
  508.     finished = pyqtSignal(dict, str)
  509.     error = pyqtSignal(str)
  510.     progress = pyqtSignal(str)
  511.     def __init__(self, phone_number, cache):
  512.         super().__init__()
  513.         self.phone_number = phone_number
  514.         self.cache = cache
  515.         self.cache_file = 'phone_cache.json'
  516.     def run(self):
  517.         self.progress.emit("Searching phone number data...")
  518.         if self.phone_number in self.cache:
  519.             self.finished.emit(self.cache[self.phone_number], self.phone_number)
  520.             return
  521.         try:
  522.             url = f"https://htmlweb.ru/geo/api.php?json&telcod={quote(self.phone_number)}"
  523.             response = requests.get(url, timeout=10)
  524.             if response.ok:
  525.                 data = response.json()
  526.                 if data.get("limit") == 0:
  527.                     self.error.emit("API limit reached. You have exhausted all request limits.")
  528.                     return
  529.                 self.cache[self.phone_number] = data
  530.                 try:
  531.                     with open(self.cache_file, 'w') as f: json.dump(self.cache, f)
  532.                 except: pass
  533.                 self.finished.emit(data, self.phone_number)
  534.             else:
  535.                 self.error.emit("Failed to fetch data. Check the phone number and try again.")
  536.         except Exception as e:
  537.             self.error.emit(f"Network error: {str(e)}")
  538.  
  539. # ==========================================
  540. # 2. MAIN WINDOW
  541. # ==========================================
  542. class WHOISViewer(QMainWindow):
  543.     def __init__(self):
  544.         super().__init__()
  545.         self.setWindowTitle("🌐 Advanced Network & WHOIS Tool")
  546.         self.resize(1200, 800)
  547.        
  548.         self.history = []
  549.         self.current_result = None
  550.         self.output_dir = Path("whois_results")
  551.         self.output_dir.mkdir(exist_ok=True)
  552.        
  553.         self.phone_cache = {}
  554.         self.phone_cache_file = 'phone_cache.json'
  555.         self._load_phone_cache()
  556.        
  557.         self.init_ui()
  558.         self.apply_theme("dark")
  559.  
  560.     def _load_phone_cache(self):
  561.         try:
  562.             with open(self.phone_cache_file, 'r') as f: self.phone_cache = json.load(f)
  563.         except: self.phone_cache = {}
  564.    
  565.     def init_ui(self):
  566.         central = QWidget()
  567.         self.setCentralWidget(central)
  568.         main_layout = QVBoxLayout(central)
  569.         main_layout.setContentsMargins(15, 15, 15, 15)
  570.         main_layout.setSpacing(10)
  571.        
  572.         top_frame = QFrame()
  573.         top_frame.setObjectName("topFrame")
  574.         top_layout = QHBoxLayout(top_frame)
  575.        
  576.         self.domain_input = QLineEdit()
  577.         self.domain_input.setPlaceholderText("Enter domain (e.g., google.com)")
  578.         self.domain_input.setObjectName("domainInput")
  579.         self.domain_input.returnPressed.connect(self.start_analysis)
  580.        
  581.         self.btn_analyze = QPushButton("🔍 Analyze")
  582.         self.btn_analyze.setObjectName("primaryBtn")
  583.         self.btn_analyze.clicked.connect(self.start_analysis)
  584.        
  585.         self.btn_export = QPushButton("💾 Export")
  586.         self.btn_export.clicked.connect(self.export_results)
  587.        
  588.         self.btn_theme = QPushButton("🌙 Theme")
  589.         self.btn_theme.clicked.connect(self.toggle_theme)
  590.        
  591.         top_layout.addWidget(QLabel("Domain:"))
  592.         top_layout.addWidget(self.domain_input, stretch=1)
  593.         top_layout.addWidget(self.btn_analyze)
  594.         top_layout.addWidget(self.btn_export)
  595.         top_layout.addWidget(self.btn_theme)
  596.        
  597.         self.progress_bar = QProgressBar()
  598.         self.progress_bar.setVisible(False)
  599.         self.progress_bar.setTextVisible(False)
  600.         self.progress_bar.setMaximumHeight(5)
  601.        
  602.         self.tabs = QTabWidget()
  603.         self.tabs.setObjectName("resultTabs")
  604.        
  605.         # WHOIS Tab
  606.         self.whois_text = QTextEdit()
  607.         self.whois_text.setReadOnly(True)
  608.         self.tabs.addTab(self.whois_text, "📋 WHOIS")
  609.        
  610.         # DNS Tab
  611.         self.dns_table = QTableWidget()
  612.         self.dns_table.setColumnCount(2)
  613.         self.dns_table.setHorizontalHeaderLabels(["Record Type", "Values"])
  614.         self.dns_table.horizontalHeader().setStretchLastSection(True)
  615.         self.tabs.addTab(self.dns_table, "🌐 DNS Records")
  616.        
  617.         # SSL Tab
  618.         self.ssl_text = QTextEdit()
  619.         self.ssl_text.setReadOnly(True)
  620.         self.tabs.addTab(self.ssl_text, "🔒 SSL Certificate")
  621.        
  622.         # Geolocation Tab
  623.         self.geo_text = QTextEdit()
  624.         self.geo_text.setReadOnly(True)
  625.         self.tabs.addTab(self.geo_text, "📍 Geolocation")
  626.        
  627.         # History Tab
  628.         self.history_table = QTableWidget()
  629.         self.history_table.setColumnCount(3)
  630.         self.history_table.setHorizontalHeaderLabels(["Domain", "Time", "Actions"])
  631.         self.history_table.horizontalHeader().setStretchLastSection(True)
  632.         self.history_table.cellClicked.connect(self.load_from_history)
  633.         self.tabs.addTab(self.history_table, "📜 History")
  634.  
  635.         # My IP Tab
  636.         ip_widget = QWidget()
  637.         ip_layout = QVBoxLayout(ip_widget)
  638.         self.btn_refresh_ip = QPushButton("🔄 Refresh IP Info")
  639.         self.btn_refresh_ip.clicked.connect(self.start_ip_lookup)
  640.         self.ip_text = QTextEdit()
  641.         self.ip_text.setReadOnly(True)
  642.         ip_layout.addWidget(self.btn_refresh_ip)
  643.         ip_layout.addWidget(self.ip_text)
  644.         self.tabs.addTab(ip_widget, "🌐 My IP")
  645.  
  646.         # Port Scanner Tab
  647.         port_widget = QWidget()
  648.         port_layout = QVBoxLayout(port_widget)
  649.         port_top = QHBoxLayout()
  650.         self.port_ip_input = QLineEdit()
  651.         self.port_ip_input.setPlaceholderText("Target IP (e.g., 192.168.1.1)")
  652.         self.port_start_input = QLineEdit("1")
  653.         self.port_end_input = QLineEdit("1024")
  654.         self.btn_start_scan = QPushButton("🚀 Start Scan")
  655.         self.btn_start_scan.clicked.connect(self.start_port_scan)
  656.         self.btn_stop_scan = QPushButton("🛑 Stop")
  657.         self.btn_stop_scan.clicked.connect(self.stop_port_scan)
  658.         self.btn_stop_scan.setEnabled(False)
  659.         port_top.addWidget(QLabel("IP:"))
  660.         port_top.addWidget(self.port_ip_input, stretch=2)
  661.         port_top.addWidget(QLabel("Start:"))
  662.         port_top.addWidget(self.port_start_input)
  663.         port_top.addWidget(QLabel("End:"))
  664.         port_top.addWidget(self.port_end_input)
  665.         port_top.addWidget(self.btn_start_scan)
  666.         port_top.addWidget(self.btn_stop_scan)
  667.         self.port_progress = QProgressBar()
  668.         self.port_progress.setVisible(False)
  669.         self.port_text = QTextEdit()
  670.         self.port_text.setReadOnly(True)
  671.         port_layout.addLayout(port_top)
  672.         port_layout.addWidget(self.port_progress)
  673.         port_layout.addWidget(self.port_text)
  674.         self.tabs.addTab(port_widget, "🚪 Port Scanner")
  675.  
  676.         # Social Media Tab
  677.         social_widget = QWidget()
  678.         social_layout = QVBoxLayout(social_widget)
  679.         social_top = QHBoxLayout()
  680.         self.social_query_input = QLineEdit()
  681.         self.social_query_input.setPlaceholderText("Enter username to search...")
  682.         self.btn_social_search = QPushButton("🔍 Search Profiles")
  683.         self.btn_social_search.clicked.connect(self.start_social_search)
  684.         social_top.addWidget(self.social_query_input, stretch=1)
  685.         social_top.addWidget(self.btn_social_search)
  686.         self.social_progress = QProgressBar()
  687.         self.social_progress.setVisible(False)
  688.         self.social_progress.setRange(0, 0)
  689.         self.social_text = QTextEdit()
  690.         self.social_text.setReadOnly(True)
  691.         social_layout.addLayout(social_top)
  692.         social_layout.addWidget(self.social_progress)
  693.         social_layout.addWidget(self.social_text)
  694.         self.tabs.addTab(social_widget, "👥 Social Media")
  695.  
  696.         # Phone Tab
  697.         phone_widget = QWidget()
  698.         phone_layout = QVBoxLayout(phone_widget)
  699.         phone_top = QHBoxLayout()
  700.         self.phone_input = QLineEdit()
  701.         self.phone_input.setPlaceholderText("Enter phone number (e.g., +79833170773)")
  702.         self.btn_phone_search = QPushButton("🔍 Lookup")
  703.         self.btn_phone_search.clicked.connect(self.start_phone_lookup)
  704.         phone_top.addWidget(self.phone_input, stretch=1)
  705.         phone_top.addWidget(self.btn_phone_search)
  706.         self.phone_progress = QProgressBar()
  707.         self.phone_progress.setVisible(False)
  708.         self.phone_progress.setRange(0, 0)
  709.         self.phone_text = QTextEdit()
  710.         self.phone_text.setReadOnly(True)
  711.         phone_layout.addLayout(phone_top)
  712.         phone_layout.addWidget(self.phone_progress)
  713.         phone_layout.addWidget(self.phone_text)
  714.         self.tabs.addTab(phone_widget, "📞 Phone")
  715.  
  716.         self.status_bar = QStatusBar()
  717.         self.setStatusBar(self.status_bar)
  718.         self.status_bar.showMessage("Ready. Enter a domain and click Analyze.")
  719.        
  720.         main_layout.addWidget(top_frame)
  721.         main_layout.addWidget(self.progress_bar)
  722.         main_layout.addWidget(self.tabs, stretch=1)
  723.    
  724.     def start_analysis(self):
  725.         domain = self.domain_input.text().strip()
  726.         if not domain:
  727.             QMessageBox.warning(self, "Warning", "Please enter a domain!")
  728.             return
  729.         domain = domain.replace('http://', '').replace('https://', '').replace('www.', '').split('/')[0]
  730.         self.progress_bar.setVisible(True)
  731.         self.progress_bar.setRange(0, 0)
  732.         self.status_bar.showMessage(f"Analyzing {domain}...")
  733.         self.worker = AnalysisWorker(domain)
  734.         self.worker.progress.connect(self.update_status)
  735.         self.worker.finished.connect(self.on_analysis_complete)
  736.         self.worker.error.connect(self.on_analysis_error)
  737.         self.worker.start()
  738.    
  739.     def update_status(self, message): self.status_bar.showMessage(message)
  740.    
  741.     def on_analysis_complete(self, result):
  742.         self.progress_bar.setVisible(False)
  743.         self.current_result = result
  744.         self.status_bar.showMessage(f"✅ Analysis complete for {result['domain']}")
  745.         self.display_whois(result['whois'])
  746.         self.display_dns(result['dns'])
  747.         self.display_ssl(result['ssl'])
  748.         self.display_geolocation(result['geolocation'])
  749.         self.add_to_history(result)
  750.    
  751.     def on_analysis_error(self, error_msg):
  752.         self.progress_bar.setVisible(False)
  753.         self.status_bar.showMessage(f"❌ Error: {error_msg}")
  754.         QMessageBox.critical(self, "Error", f"Analysis failed:\n{error_msg}")
  755.    
  756.     def display_whois(self, whois_data):
  757.         self.whois_text.clear()
  758.         if 'error' in whois_data:
  759.             self.whois_text.setHtml(f"<p style='color: red;'>❌ {whois_data['error']}</p>")
  760.             return
  761.         if whois_data.get('method') == 'rdap' and 'data' in whois_data:
  762.             self.display_rdap_data(whois_data['data'])
  763.         elif 'raw' in whois_data:
  764.             self.whois_text.setPlainText(whois_data['raw'])
  765.         else:
  766.             self.whois_text.setPlainText("No WHOIS data available")
  767.  
  768.     def display_rdap_data(self, data):
  769.         html = "<html><body style='font-family: Consolas, monospace;'><h3>📋 WHOIS Information (RDAP)</h3><table style='border-collapse: collapse; width: 100%;'>"
  770.         html += self.add_table_row("Domain Name", data.get('ldhName', 'N/A'))
  771.         registrar, abuse_email, abuse_phone = "N/A", "N/A", "N/A"
  772.         for entity in data.get('entities', []):
  773.             if 'registrar' in entity.get('roles', []):
  774.                 vcard = entity.get('vcardArray', [])
  775.                 if len(vcard) > 1:
  776.                     for item in vcard[1]:
  777.                         if item[0] == 'fn': registrar = item[3]
  778.                 for sub_entity in entity.get('entities', []):
  779.                     if 'abuse' in sub_entity.get('roles', []):
  780.                         sub_vcard = sub_entity.get('vcardArray', [])
  781.                         if len(sub_vcard) > 1:
  782.                             for item in sub_vcard[1]:
  783.                                 if item[0] == 'email': abuse_email = item[3]
  784.                                 elif item[0] == 'tel': abuse_phone = item[3].replace('tel:', '')
  785.         html += self.add_table_row("Registrar", registrar)
  786.         html += self.add_table_row("Abuse Contact Email", abuse_email)
  787.         html += self.add_table_row("Abuse Contact Phone", abuse_phone)
  788.         status = data.get('status', [])
  789.         if status: html += self.add_table_row("Status", "<br>".join([f"• {s}" for s in status]))
  790.         events = data.get('events', [])
  791.         event_labels = {'registration': 'Registration Date', 'expiration': 'Expiration Date', 'last changed': 'Last Updated', 'last update of RDAP database': 'RDAP Database Updated'}
  792.         for event in events:
  793.             action, date_str = event.get('eventAction', ''), event.get('eventDate', 'N/A')
  794.             if date_str != 'N/A':
  795.                 try: dt = datetime.fromisoformat(date_str.replace('Z', '+00:00')); formatted_date = dt.strftime('%Y-%m-%d %H:%M:%S UTC')
  796.                 except: formatted_date = date_str
  797.                 html += self.add_table_row(event_labels.get(action, action.title()), formatted_date)
  798.         nameservers = data.get('nameservers', [])
  799.         if nameservers: html += self.add_table_row("Nameservers", "<br>".join([ns.get('ldhName', '').lower() for ns in nameservers]))
  800.         secure_dns = data.get('secureDNS', {})
  801.         html += self.add_table_row("DNSSEC", "Signed (Enabled)" if secure_dns.get('delegationSigned') else "Unsigned (Disabled)")
  802.         html += "</table>"
  803.         raw_json_escaped = json.dumps(data, indent=2).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
  804.         html += "<br><hr><h4>📄 Raw JSON Data</h4>"
  805.         html += f"<pre style='padding: 10px; border-radius: 4px; overflow-x: auto; margin-top: 5px; font-size: 11px; white-space: pre-wrap; word-break: break-all;'>{raw_json_escaped}</pre></body></html>"
  806.         self.whois_text.setHtml(html)
  807.  
  808.     def add_table_row(self, label, value):
  809.         return f"<tr><td style='padding: 8px; font-weight: bold; border: 1px solid #ddd; width: 220px; vertical-align: top;'>{label}:</td><td style='padding: 8px; border: 1px solid #ddd; word-break: break-all;'>{value}</td></tr>"
  810.  
  811.     def display_dns(self, dns_data):
  812.         self.dns_table.setRowCount(0)
  813.         if 'error' in dns_data: return
  814.         if dns_data.get('ip_address'):
  815.             row = self.dns_table.rowCount()
  816.             self.dns_table.insertRow(row)
  817.             self.dns_table.setItem(row, 0, QTableWidgetItem("IP Address"))
  818.             self.dns_table.setItem(row, 1, QTableWidgetItem(dns_data['ip_address']))
  819.         for record_type in ['a', 'aaaa', 'mx', 'ns', 'txt', 'cname']:
  820.             records = dns_data.get(record_type, [])
  821.             if records:
  822.                 row = self.dns_table.rowCount()
  823.                 self.dns_table.insertRow(row)
  824.                 self.dns_table.setItem(row, 0, QTableWidgetItem(record_type.upper()))
  825.                 self.dns_table.setItem(row, 1, QTableWidgetItem(', '.join(records)))
  826.    
  827.     def display_ssl(self, ssl_data):
  828.         self.ssl_text.clear()
  829.         if 'error' in ssl_data:
  830.             self.ssl_text.setHtml(f"<p style='color: red;'>❌ {ssl_data['error']}</p>")
  831.             return
  832.         html = "<html><body style='font-family: Consolas, monospace;'><h3>🔒 SSL Certificate Information</h3><table style='border-collapse: collapse; width: 100%;'>"
  833.         for label, value in [('Issuer', ssl_data.get('issuer')), ('Subject', ssl_data.get('subject')), ('Serial Number', ssl_data.get('serial')), ('Valid From', ssl_data.get('valid_from')), ('Valid Until', ssl_data.get('valid_until')), ('Protocol', ssl_data.get('protocol')), ('Cipher', str(ssl_data.get('cipher')))]:
  834.             if value: html += f"<tr><td style='padding: 8px; font-weight: bold; border: 1px solid #ddd;'>{label}:</td><td style='padding: 8px; border: 1px solid #ddd;'>{value}</td></tr>"
  835.         days = ssl_data.get('days_until_expiry', 0)
  836.         color = 'green' if days > 30 else 'orange' if days > 0 else 'red'
  837.         html += f"<tr><td style='padding: 8px; font-weight: bold; border: 1px solid #ddd;'>Days Until Expiry:</td><td style='padding: 8px; border: 1px solid #ddd; color: {color}; font-weight: bold;'>{days} days</td></tr></table></body></html>"
  838.         self.ssl_text.setHtml(html)
  839.    
  840.     def display_geolocation(self, geo_data):
  841.         self.geo_text.clear()
  842.         if 'error' in geo_data:
  843.             self.geo_text.setHtml(f"<p style='color: red;'>❌ {geo_data['error']}</p>")
  844.             return
  845.         html = "<html><body style='font-family: Consolas, monospace;'><h3>📍 Geolocation Information</h3><table style='border-collapse: collapse; width: 100%;'>"
  846.         for label, value in [('IP Address', geo_data.get('ip')), ('Country', geo_data.get('country')), ('Region', geo_data.get('region')), ('City', geo_data.get('city')), ('Coordinates', f"{geo_data.get('latitude')}, {geo_data.get('longitude')}"), ('ISP', geo_data.get('isp')), ('Organization', geo_data.get('org')), ('Timezone', geo_data.get('timezone'))]:
  847.             if value: html += f"<tr><td style='padding: 8px; font-weight: bold; border: 1px solid #ddd;'>{label}:</td><td style='padding: 8px; border: 1px solid #ddd;'>{value}</td></tr>"
  848.         html += "</table></body></html>"
  849.         self.geo_text.setHtml(html)
  850.    
  851.     def add_to_history(self, result):
  852.         self.history.append(result)
  853.         row = self.history_table.rowCount()
  854.         self.history_table.insertRow(row)
  855.         self.history_table.setItem(row, 0, QTableWidgetItem(result['domain']))
  856.         self.history_table.setItem(row, 1, QTableWidgetItem(result['timestamp'][:19]))
  857.         btn_view = QPushButton("View")
  858.         btn_view.clicked.connect(lambda: self.load_from_history(row, 0))
  859.         self.history_table.setCellWidget(row, 2, btn_view)
  860.    
  861.     def load_from_history(self, row, col):
  862.         if row < len(self.history):
  863.             result = self.history[row]
  864.             self.current_result = result
  865.             self.domain_input.setText(result['domain'])
  866.             self.display_whois(result['whois'])
  867.             self.display_dns(result['dns'])
  868.             self.display_ssl(result['ssl'])
  869.             self.display_geolocation(result['geolocation'])
  870.             self.tabs.setCurrentIndex(0)
  871.    
  872.     def export_results(self):
  873.         if not self.current_result:
  874.             QMessageBox.warning(self, "Warning", "No results to export!")
  875.             return
  876.         file_path, selected_filter = QFileDialog.getSaveFileName(self, "Export Results", "", "JSON Files (*.json);;CSV Files (*.csv);;Text Files (*.txt)")
  877.         if not file_path: return
  878.         try:
  879.             if selected_filter.startswith("JSON"):
  880.                 with open(file_path, 'w', encoding='utf-8') as f: json.dump(self.current_result, f, indent=2, default=str)
  881.             elif selected_filter.startswith("CSV"):
  882.                 with open(file_path, 'w', newline='', encoding='utf-8') as f:
  883.                     writer = csv.writer(f)
  884.                     writer.writerow(['Field', 'Value'])
  885.                     self.write_dict_to_csv(writer, self.current_result)
  886.             elif selected_filter.startswith("Text"):
  887.                 with open(file_path, 'w', encoding='utf-8') as f: f.write(json.dumps(self.current_result, indent=2, default=str))
  888.             self.status_bar.showMessage(f"✅ Exported to {file_path}")
  889.             QMessageBox.information(self, "Success", f"Results exported successfully!")
  890.         except Exception as e:
  891.             QMessageBox.critical(self, "Error", f"Failed to export:\n{str(e)}")
  892.    
  893.     def write_dict_to_csv(self, writer, data, prefix=''):
  894.         for key, value in data.items():
  895.             full_key = f"{prefix}{key}" if prefix else key
  896.             if isinstance(value, dict): self.write_dict_to_csv(writer, value, f"{full_key}.")
  897.             elif isinstance(value, list): writer.writerow([full_key, ', '.join(map(str, value))])
  898.             else: writer.writerow([full_key, value])
  899.  
  900.     # ================= TAB METHODS =================
  901.    
  902.     def start_ip_lookup(self):
  903.         self.ip_text.setHtml("<p>Fetching IP and system information...</p>")
  904.         self.btn_refresh_ip.setEnabled(False)
  905.         self.ip_worker = GetIPWorker()
  906.         self.ip_worker.finished.connect(self.on_ip_lookup_complete)
  907.         self.ip_worker.error.connect(self.on_ip_lookup_error)
  908.         self.ip_worker.start()
  909.  
  910.     def on_ip_lookup_complete(self, info):
  911.         self.btn_refresh_ip.setEnabled(True)
  912.         html = "<html><body style='font-family: Consolas, monospace;'><h3>🌐 Network & System Information</h3><table style='border-collapse: collapse; width: 100%;'>"
  913.         for label, value in [('PC Hostname', info.get('hostname')), ('PC Username', info.get('username')), ('PC Display Name', info.get('display_name')), ('Public IP [IPv4]', info.get('public_ip')), ('Local IP [IPv4]', info.get('local_ip')), ('IPv6 Addresses', info.get('ipv6'))]:
  914.             html += f"<tr><td style='padding: 8px; font-weight: bold; border: 1px solid #ddd; width: 200px;'>{label}:</td><td style='padding: 8px; border: 1px solid #ddd;'>{value}</td></tr>"
  915.         html += "</table></body></html>"
  916.         self.ip_text.setHtml(html)
  917.  
  918.     def on_ip_lookup_error(self, error):
  919.         self.btn_refresh_ip.setEnabled(True)
  920.         self.ip_text.setHtml(f"<p style='color: red;'>❌ Error: {error}</p>")
  921.  
  922.     def start_port_scan(self):
  923.         ip = self.port_ip_input.text().strip()
  924.         if not ip:
  925.             QMessageBox.warning(self, "Warning", "Please enter a target IP!")
  926.             return
  927.         try:
  928.             start_p = int(self.port_start_input.text())
  929.             end_p = int(self.port_end_input.text())
  930.             if start_p < 1 or end_p > 65535 or start_p > end_p: raise ValueError
  931.         except:
  932.             QMessageBox.warning(self, "Warning", "Invalid port range! (1-65535)")
  933.             return
  934.         self.port_text.clear()
  935.         self.port_progress.setVisible(True)
  936.         self.port_progress.setValue(0)
  937.         self.btn_start_scan.setEnabled(False)
  938.         self.btn_stop_scan.setEnabled(True)
  939.         self.port_worker = PortScannerWorker(ip, start_p, end_p)
  940.         self.port_worker.progress.connect(self.port_progress.setValue)
  941.         self.port_worker.status.connect(lambda msg: self.port_text.append(msg))
  942.         self.port_worker.finished.connect(self.on_port_scan_complete)
  943.         self.port_worker.error.connect(self.on_port_scan_error)
  944.         self.port_worker.start()
  945.  
  946.     def stop_port_scan(self):
  947.         if hasattr(self, 'port_worker'):
  948.             self.port_worker.stop()
  949.             self.port_text.append("<p style='color: orange;'>Stopping scan...</p>")
  950.  
  951.     def on_port_scan_complete(self, open_ports):
  952.         self.port_progress.setVisible(False)
  953.         self.btn_start_scan.setEnabled(True)
  954.         self.btn_stop_scan.setEnabled(False)
  955.         if not open_ports:
  956.             self.port_text.append("<p style='color: orange;'>No open ports found in the specified range.</p>")
  957.             return
  958.         html = "<br><h3>🚪 Open Ports Summary</h3><table style='border-collapse: collapse; width: 100%;'><tr><th style='padding: 8px; border: 1px solid #ddd; background-color: #2ea043; color: white;'>Port</th><th style='padding: 8px; border: 1px solid #ddd; background-color: #2ea043; color: white;'>Status</th></tr>"
  959.         for port in open_ports:
  960.             html += f"<tr><td style='padding: 8px; border: 1px solid #ddd;'>{port}</td><td style='padding: 8px; border: 1px solid #ddd; color: green;'>OPEN</td></tr>"
  961.         html += "</table>"
  962.         self.port_text.append(html)
  963.  
  964.     def on_port_scan_error(self, error):
  965.         self.port_progress.setVisible(False)
  966.         self.btn_start_scan.setEnabled(True)
  967.         self.btn_stop_scan.setEnabled(False)
  968.         self.port_text.setHtml(f"<p style='color: red;'>❌ Error: {error}</p>")
  969.  
  970.     def start_social_search(self):
  971.         query = self.social_query_input.text().strip()
  972.         if not query:
  973.             QMessageBox.warning(self, "Warning", "Please enter a username!")
  974.             return
  975.         self.social_text.clear()
  976.         self.social_progress.setVisible(True)
  977.         self.btn_social_search.setEnabled(False)
  978.         self.social_worker = SocialMediaWorker(query)
  979.         self.social_worker.progress.connect(self.update_social_progress)
  980.         self.social_worker.finished.connect(self.on_social_search_complete)
  981.         self.social_worker.error.connect(self.on_social_search_error)
  982.         self.social_worker.start()
  983.  
  984.     def update_social_progress(self, message):
  985.         self.social_text.append(message)
  986.  
  987.     def on_social_search_complete(self, results):
  988.         self.social_progress.setVisible(False)
  989.         self.btn_social_search.setEnabled(True)
  990.         if not results:
  991.             self.social_text.setHtml("<p style='color: orange;'>No profiles found.</p>")
  992.             return
  993.         html = "<html><body style='font-family: Consolas, monospace;'><h3>👥 Found Social Media Profiles</h3>"
  994.         html += f"<p style='color: green; font-weight: bold;'>Found {len(results)} profile(s):</p>"
  995.         html += "<table style='border-collapse: collapse; width: 100%;'>"
  996.         for platform, url in sorted(results):
  997.             html += f"<tr><td style='padding: 8px; border: 1px solid #ddd; font-weight: bold;'>{platform}:</td><td style='padding: 8px; border: 1px solid #ddd;'><a href='{url}' style='color: #007acc;'>{url}</a></td></tr>"
  998.         html += "</table></body></html>"
  999.         self.social_text.setHtml(html)
  1000.  
  1001.     def on_social_search_error(self, error):
  1002.         self.social_progress.setVisible(False)
  1003.         self.btn_social_search.setEnabled(True)
  1004.         self.social_text.setHtml(f"<p style='color: red;'>❌ Error: {error}</p>")
  1005.  
  1006.     def start_phone_lookup(self):
  1007.         phone = self.phone_input.text().strip()
  1008.         if not phone:
  1009.             QMessageBox.warning(self, "Warning", "Please enter a phone number!")
  1010.             return
  1011.         self.phone_text.clear()
  1012.         self.phone_progress.setVisible(True)
  1013.         self.btn_phone_search.setEnabled(False)
  1014.         self.phone_worker = PhoneWorker(phone, self.phone_cache)
  1015.         self.phone_worker.progress.connect(lambda msg: self.status_bar.showMessage(msg))
  1016.         self.phone_worker.finished.connect(self.on_phone_lookup_complete)
  1017.         self.phone_worker.error.connect(self.on_phone_lookup_error)
  1018.         self.phone_worker.start()
  1019.  
  1020.     def on_phone_lookup_complete(self, data, phone_number):
  1021.         self.phone_progress.setVisible(False)
  1022.         self.btn_phone_search.setEnabled(True)
  1023.         self.status_bar.showMessage("✅ Phone lookup complete.")
  1024.         self.display_phone_info(data, phone_number)
  1025.  
  1026.     def on_phone_lookup_error(self, error):
  1027.         self.phone_progress.setVisible(False)
  1028.         self.btn_phone_search.setEnabled(True)
  1029.         self.phone_text.setHtml(f"<p style='color: red;'>❌ Error: {error}</p>")
  1030.         self.status_bar.showMessage(f"❌ Error: {error}")
  1031.  
  1032.     def display_phone_info(self, data, phone_number):
  1033.         html = "<html><body style='font-family: Consolas, monospace;'>"
  1034.         html += f"<h3>📞 Phone Number Information: {phone_number}</h3>"
  1035.         html += "<table style='border-collapse: collapse; width: 100%;'>"
  1036.        
  1037.         country = data.get('country', {})
  1038.         region = data.get('region', {})
  1039.         other = data.get('0', {})
  1040.         capital = data.get('capital', {})
  1041.        
  1042.         def get_val(d, key, default='N/A'):
  1043.             val = d.get(key)
  1044.             return val if val else default
  1045.  
  1046.         # Russian ko English mein translate karne ke liye maps
  1047.         lang_map = {
  1048.             'ru': 'Russian', 'en': 'English', 'fa': 'Persian', 'ar': 'Arabic',
  1049.             'fr': 'French', 'de': 'German', 'es': 'Spanish', 'zh': 'Chinese',
  1050.             'hi': 'Hindi', 'pt': 'Portuguese', 'ja': 'Japanese', 'tr': 'Turkish',
  1051.             'ko': 'Korean', 'it': 'Italian', 'nl': 'Dutch', 'pl': 'Polish'
  1052.         }
  1053.         continent_map = {
  1054.             'Европа': 'Europe', 'Азия': 'Asia', 'Африка': 'Africa',
  1055.             'Северная Америка': 'North America', 'Южная Америка': 'South America',
  1056.             'Австралия': 'Australia', 'Антарктида': 'Antarctica'
  1057.         }
  1058.         okrug_map = {
  1059.             'Центральный федеральный округ': 'Central Federal District',
  1060.             'Южный федеральный округ': 'Southern Federal District',
  1061.             'Северо-Кавказский федеральный округ': 'North Caucasian Federal District',
  1062.             'Северо-Западный федеральный округ': 'Northwestern Federal District',
  1063.             'Приволжский федеральный округ': 'Volga Federal District',
  1064.             'Уральский федеральный округ': 'Ural Federal District',
  1065.             'Сибирский федеральный округ': 'Siberian Federal District',
  1066.             'Дальневосточный федеральный округ': 'Far Eastern Federal District'
  1067.         }
  1068.  
  1069.         lang_code = get_val(country, 'langcod')
  1070.         lang_display = f"{lang_map.get(lang_code, lang_code.upper())} ({lang_code})" if lang_code != 'N/A' else 'N/A'
  1071.         continent = continent_map.get(data.get('location'), data.get('location', 'N/A'))
  1072.         okrug = okrug_map.get(region.get('okrug'), region.get('okrug', 'N/A'))
  1073.  
  1074.         # API ki 'english' fields use ki hain taake result English mein aaye
  1075.         fields = [
  1076.             ("Country", get_val(country, 'english')),
  1077.             ("Continent", continent),
  1078.             ("Region / State", get_val(region, 'english')),
  1079.             ("Federal District", okrug),
  1080.             ("City", get_val(other, 'english')),
  1081.             ("Postal Code", get_val(other, 'post')),
  1082.             ("ISO Code", get_val(country, 'iso')),
  1083.             ("Country Code", get_val(country, 'telcod')),
  1084.             ("Area Code", get_val(other, 'telcod')),
  1085.             ("Region Auto Code", get_val(region, 'autocod')),
  1086.             ("Operator", f"{get_val(other, 'oper_brand')} (DEF: {get_val(other, 'def')})"),
  1087.             ("Location", f"{get_val(country, 'english')}, {get_val(region, 'english')}, {get_val(other, 'english')}"),
  1088.             ("Capital", get_val(capital, 'english')),
  1089.             ("Language", lang_display),
  1090.             ("Timezone", get_val(other, 'tz')),
  1091.             ("Coordinates", f"{get_val(other, 'latitude')}, {get_val(other, 'longitude')}"),
  1092.             ("Wiki Link", f"<a href='https://{get_val(other, 'wiki')}' style='color: #007acc;'>{get_val(other, 'wiki')}</a>" if get_val(other, 'wiki') != 'N/A' else 'N/A'),
  1093.             ("Google Maps", f"<a href='https://www.google.com/maps/place/{get_val(other, 'latitude')}+{get_val(other, 'longitude')}' style='color: #007acc;'>Open in Maps</a>" if get_val(other, 'latitude') != 'N/A' else 'N/A'),
  1094.             ("Phone Radar", f"<a href='https://phoneradar.ru/phone/{phone_number}' style='color: #007acc;'>View on Phone Radar</a>")
  1095.         ]
  1096.        
  1097.         ignore_patterns = ['N/A', 'N/A (DEF: N/A)', 'N/A, N/A, N/A', ' ()', 'N/A (N/A)']
  1098.         for label, value in fields:
  1099.             if value and str(value).strip() not in ignore_patterns:
  1100.                 html += self.add_table_row(label, value)
  1101.                
  1102.         html += "</table></body></html>"
  1103.         self.phone_text.setHtml(html)
  1104.  
  1105.     def toggle_theme(self):
  1106.         current = self.property("theme")
  1107.         if current == "dark": self.apply_theme("light")
  1108.         else: self.apply_theme("dark")
  1109.    
  1110.     def apply_theme(self, theme):
  1111.         self.setProperty("theme", theme)
  1112.         if theme == "dark":
  1113.             qss = """
  1114.                QMainWindow, QWidget { background-color: #1e1e1e; color: #d4d4d4; }
  1115.                QFrame#topFrame { background-color: #252526; border: 1px solid #333; border-radius: 8px; }
  1116.                QLineEdit#domainInput, QLineEdit { background-color: #3c3c3c; color: white; border: 1px solid #555; padding: 8px; border-radius: 4px; font-size: 14px; }
  1117.                QPushButton { background-color: #0e639c; color: white; border: none; padding: 8px 20px; border-radius: 5px; font-weight: bold; }
  1118.                QPushButton:hover { background-color: #1177bb; }
  1119.                QPushButton:disabled { background-color: #555; color: #999; }
  1120.                QPushButton#primaryBtn { background-color: #2ea043; }
  1121.                QPushButton#primaryBtn:hover { background-color: #3fb950; }
  1122.                QTabWidget::pane { border: 1px solid #333; background-color: #1e1e1e; }
  1123.                QTabBar::tab { background-color: #252526; color: #d4d4d4; padding: 10px 20px; border: 1px solid #333; border-bottom: none; }
  1124.                QTabBar::tab:selected { background-color: #0e639c; color: white; }
  1125.                QTextEdit { background-color: #1e1e1e; color: #d4d4d4; border: 1px solid #333; font-family: Consolas, monospace; }
  1126.                QTableWidget { background-color: #1e1e1e; color: #d4d4d4; border: 1px solid #333; }
  1127.                QTableWidget::item { padding: 5px; }
  1128.                QHeaderView::section { background-color: #2d2d2d; color: white; padding: 8px; border: 1px solid #333; font-weight: bold; }
  1129.                QProgressBar { border: none; background-color: #333; }
  1130.                QProgressBar::chunk { background-color: #007acc; }
  1131.                QStatusBar { background-color: #007acc; color: white; font-weight: bold; }
  1132.            """
  1133.             self.btn_theme.setText("☀️ Light")
  1134.         else:
  1135.             qss = """
  1136.                QMainWindow, QWidget { background-color: #f3f3f3; color: #333; }
  1137.                QFrame#topFrame { background-color: #ffffff; border: 1px solid #ddd; border-radius: 8px; }
  1138.                QLineEdit#domainInput, QLineEdit { background-color: #fff; color: #333; border: 1px solid #ccc; padding: 8px; border-radius: 4px; font-size: 14px; }
  1139.                QPushButton { background-color: #0078d4; color: white; border: none; padding: 8px 20px; border-radius: 5px; font-weight: bold; }
  1140.                QPushButton:hover { background-color: #005a9e; }
  1141.                QPushButton:disabled { background-color: #ccc; color: #666; }
  1142.                QPushButton#primaryBtn { background-color: #107c10; }
  1143.                QPushButton#primaryBtn:hover { background-color: #0b5a0b; }
  1144.                QTabWidget::pane { border: 1px solid #ddd; background-color: #ffffff; }
  1145.                QTabBar::tab { background-color: #e1e1e1; color: #333; padding: 10px 20px; border: 1px solid #ddd; border-bottom: none; }
  1146.                QTabBar::tab:selected { background-color: #0078d4; color: white; }
  1147.                QTextEdit { background-color: #ffffff; color: #333; border: 1px solid #ddd; font-family: Consolas, monospace; }
  1148.                QTableWidget { background-color: #ffffff; color: #333; border: 1px solid #ddd; }
  1149.                QTableWidget::item { padding: 5px; }
  1150.                QHeaderView::section { background-color: #e1e1e1; color: #333; padding: 8px; border: 1px solid #ddd; font-weight: bold; }
  1151.                QProgressBar { border: none; background-color: #ddd; }
  1152.                QProgressBar::chunk { background-color: #0078d4; }
  1153.                QStatusBar { background-color: #0078d4; color: white; font-weight: bold; }
  1154.            """
  1155.             self.btn_theme.setText("🌙 Dark")
  1156.         self.setStyleSheet(qss)
  1157.  
  1158. if __name__ == "__main__":
  1159.     app = QApplication(sys.argv)
  1160.     app.setFont(QFont("Segoe UI", 10))
  1161.     viewer = WHOISViewer()
  1162.     viewer.show()
  1163.     sys.exit(app.exec_())
Advertisement
Add Comment
Please, Sign In to add comment