diff --git a/.pylintrc b/.pylintrc index 6b37134..140c2d3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,3 +1,3 @@ [MESSAGES CONTROL] disable = C0413, W1203, W0703, R1710, R0903, R1732, - R0201, W1508, R0801, R0902 + R0201, W1508, R0801, R0902, R0914 diff --git a/CHANGELOG.md b/CHANGELOG.md index c07f47f..b7bf0fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,60 +1,65 @@ -# gods_eye changelog - -## 1.1.2 -- Changed README - -## 1.1.1 -- Remove unused dependencies - -## 1.1.0 -- Update pipeline - -## 1.0.9 -- Add one_sec_mail tool. -- Update README - -## 1.0.8 -- Add status badge Markdown - -## 1.0.7 -- Fix release job to publish release package. - -## 1.0.6 -- Add pipeline. - Add deploy to PyPI. - Add tests and code check. - -## 1.0.5 -- Pylint check code style - -## 1.0.4 -- `phone_info` modification. -Now the map can be saved in the specified path. - -## 1.0.3 -- Added tools: - - phone_info -Gets country, operator and location by phone number. - -## 1.0.2 -- Added tools: - - pwned -Checks if your password has been compromised in a data breach. - -## 1.0.1 -- Added tools: - - ip_info_finder -This tool works for Domain or IP addresses - -- Added error handler - -## 1.0.0 -- Added tools: - - clickjacking - - exec_shell_command - - http_headers_grabber - - ip - - logger - - nmap_scanner - - robots_scaner +# gods_eye changelog + +## 1.2.0 + +- Add ProbivApiPhoneInfo class for searching information by phone number using probivapi.com +- Fix code style + +## 1.1.2 +- Changed README + +## 1.1.1 +- Remove unused dependencies + +## 1.1.0 +- Update pipeline + +## 1.0.9 +- Add one_sec_mail tool. +- Update README + +## 1.0.8 +- Add status badge Markdown + +## 1.0.7 +- Fix release job to publish release package. + +## 1.0.6 +- Add pipeline. + Add deploy to PyPI. + Add tests and code check. + +## 1.0.5 +- Pylint check code style + +## 1.0.4 +- `phone_info` modification. +Now the map can be saved in the specified path. + +## 1.0.3 +- Added tools: + - phone_info +Gets country, operator and location by phone number. + +## 1.0.2 +- Added tools: + - pwned +Checks if your password has been compromised in a data breach. + +## 1.0.1 +- Added tools: + - ip_info_finder +This tool works for Domain or IP addresses + +- Added error handler + +## 1.0.0 +- Added tools: + - clickjacking + - exec_shell_command + - http_headers_grabber + - ip + - logger + - nmap_scanner + - robots_scaner - whois_lookup \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index d3d231f..23af10c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = gods_eye -version = 1.1.2 +version = 1.2.0 author = Pavel Dat author_email = dats.pavel1999@gmail.com description = A set of tools which should be used in Gods Eye diff --git a/src/clickjacking/clickjacking.py b/src/clickjacking/clickjacking.py index c7f4fe5..45a042f 100644 --- a/src/clickjacking/clickjacking.py +++ b/src/clickjacking/clickjacking.py @@ -7,19 +7,15 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import logging +import sys -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") -from logger.logger import Logger from http_headers_grabber.http_headers_grabber import HttpHeadersGrabber +from logger.logger import Logger - -logger = Logger('ClickJacking') +logger = Logger("ClickJacking") class ClickJacking: @@ -44,24 +40,28 @@ def click_jacking(target: str, debug: bool = False) -> bool: logger.setLevel(logging.DEBUG) if not isinstance(target, str): - logger.raise_fatal(BaseException(f'Target must be a string not {type(target)}. ' - f'Got target: {target}')) + logger.raise_fatal( + BaseException( + f"Target must be a string not {type(target)}. " + f"Got target: {target}" + ) + ) target = target.lower() - if not (target.startswith('http://') or target.startswith('https://')): - target = 'http://' + target + if not (target.startswith("http://") or target.startswith("https://")): + target = "http://" + target - logger.info(f'Testing ClickJacking for {target}') + logger.info(f"Testing ClickJacking for {target}") try: headers = HttpHeadersGrabber.http_headers_grabber(target) - if 'X-Frame-Options' in headers.keys(): - logger.debug('ClickJacking Header is present') - logger.debug('You can\'t clickjack this domain') + if "X-Frame-Options" in headers.keys(): + logger.debug("ClickJacking Header is present") + logger.debug("You can't clickjack this domain") return False - logger.debug('ClickJacking Header is missing') - logger.debug('This domain is vulnerable to ClickJacking') + logger.debug("ClickJacking Header is missing") + logger.debug("This domain is vulnerable to ClickJacking") return True except Exception as ex: - logger.raise_fatal(BaseException(f'Error occurred: {ex}')) + logger.raise_fatal(BaseException(f"Error occurred: {ex}")) diff --git a/src/dns_lookup/dns_lookup.py b/src/dns_lookup/dns_lookup.py index 03772a0..d58aed7 100644 --- a/src/dns_lookup/dns_lookup.py +++ b/src/dns_lookup/dns_lookup.py @@ -7,20 +7,16 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ - -import sys import logging +import sys + import dns.resolver -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('DnsLookup') +logger = Logger("DnsLookup") class DnsLookup: @@ -35,8 +31,7 @@ class DnsLookup: """ @staticmethod - def dns_lookup(target: str, record_type: str = 'A', - debug: bool = False) -> list: + def dns_lookup(target: str, record_type: str = "A", debug: bool = False) -> list: """ Looks for dns lookup information for IP or Domain. @@ -53,16 +48,20 @@ def dns_lookup(target: str, record_type: str = 'A', logger.setLevel(logging.DEBUG) if not isinstance(target, str): - logger.raise_fatal(BaseException(f'Target must be a string not {type(target)}. ' - f'Got target: {target}')) + logger.raise_fatal( + BaseException( + f"Target must be a string not {type(target)}. " + f"Got target: {target}" + ) + ) ipvs = [] - logger.info(f'DNS Lookup: {target.lower()}') - logger.info(f'Records to find out: {record_type}') + logger.info(f"DNS Lookup: {target.lower()}") + logger.info(f"Records to find out: {record_type}") try: for ipval in dns.resolver.resolve(target.lower(), record_type): ipvs.append(ipval.to_text()) - logger.debug(record_type + ' : ' + ipval.to_text()) + logger.debug(record_type + " : " + ipval.to_text()) return ipvs except Exception as ex: - logger.raise_fatal(BaseException(f'Error occurred: {ex}')) + logger.raise_fatal(BaseException(f"Error occurred: {ex}")) diff --git a/src/exec_shell_command/exec_shell_command.py b/src/exec_shell_command/exec_shell_command.py index d5bb35c..8d0ca73 100644 --- a/src/exec_shell_command/exec_shell_command.py +++ b/src/exec_shell_command/exec_shell_command.py @@ -7,19 +7,15 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import logging -from subprocess import Popen, PIPE +import sys +from subprocess import PIPE, Popen -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('exec_commands') +logger = Logger("exec_commands") def exec_shell_command(command: str, debug: bool = False) -> str: @@ -38,7 +34,7 @@ def exec_shell_command(command: str, debug: bool = False) -> str: if debug: logger.setLevel(logging.DEBUG) - logger.info(f'Executing command: `{command}`') + logger.info(f"Executing command: `{command}`") with Popen(command, shell=True, stdout=PIPE, stderr=PIPE) as proc: status = proc.wait() output = proc.stdout.read().decode(sys.stdout.encoding) diff --git a/src/http_headers_grabber/http_headers_grabber.py b/src/http_headers_grabber/http_headers_grabber.py index 311674f..f226d32 100644 --- a/src/http_headers_grabber/http_headers_grabber.py +++ b/src/http_headers_grabber/http_headers_grabber.py @@ -7,19 +7,16 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import logging +import sys + import requests -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('HttpGrabber') +logger = Logger("HttpGrabber") class HttpHeadersGrabber: @@ -44,15 +41,19 @@ def http_headers_grabber(target: str, debug: bool = False) -> dict: logger.setLevel(logging.DEBUG) if not isinstance(target, str): - logger.raise_fatal(BaseException(f'Target must be a string not {type(target)}. ' - f'Got target: {target}')) + logger.raise_fatal( + BaseException( + f"Target must be a string not {type(target)}. " + f"Got target: {target}" + ) + ) try: - if not (target.startswith('http://') or target.startswith('https://')): - target = 'http://' + target + if not (target.startswith("http://") or target.startswith("https://")): + target = "http://" + target response = requests.get(target.lower()) - logger.info(f'Got {target} request: {response.status_code}') - logger.debug(f'Headers:\n {response.headers}') + logger.info(f"Got {target} request: {response.status_code}") + logger.debug(f"Headers:\n {response.headers}") return response.headers except Exception as ex: - logger.raise_fatal(BaseException(f'Error occurred: {ex}')) + logger.raise_fatal(BaseException(f"Error occurred: {ex}")) diff --git a/src/ip/ip.py b/src/ip/ip.py index 5930daa..5e23429 100644 --- a/src/ip/ip.py +++ b/src/ip/ip.py @@ -7,21 +7,17 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys -import socket import logging import re as r +import socket +import sys from urllib.request import urlopen -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('GetHostname') +logger = Logger("GetHostname") class GetHostname: @@ -37,9 +33,9 @@ def get_hostname(self) -> str: * Hostname """ - logger.info('Getting hostname') + logger.info("Getting hostname") hostname = socket.gethostname() - logger.debug(f'Hostname: {hostname}') + logger.debug(f"Hostname: {hostname}") return hostname def get_ip(self) -> str: @@ -50,11 +46,10 @@ def get_ip(self) -> str: * Local IP """ - logger.info('Getting IP') - request = str(urlopen('http://checkip.dyndns.com/').read()) - local_ip = r.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search( - request).group(1) - logger.debug(f'IP: {local_ip}') + logger.info("Getting IP") + request = str(urlopen("http://checkip.dyndns.com/").read()) + local_ip = r.compile(r"Address: (\d+\.\d+\.\d+\.\d+)").search(request).group(1) + logger.debug(f"IP: {local_ip}") return local_ip @staticmethod @@ -76,4 +71,4 @@ def get_hostname_ip(debug: bool = False) -> tuple: hostname_ip = GetHostname() return hostname_ip.get_hostname(), hostname_ip.get_ip() except Exception as ex: - logger.raise_fatal(BaseException(f'Error occurred: {ex}')) + logger.raise_fatal(BaseException(f"Error occurred: {ex}")) diff --git a/src/ip_info_finder/ip_info_finder.py b/src/ip_info_finder/ip_info_finder.py index 62314ff..dec51e2 100644 --- a/src/ip_info_finder/ip_info_finder.py +++ b/src/ip_info_finder/ip_info_finder.py @@ -7,21 +7,17 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import json import logging +import sys import urllib.request from urllib.error import URLError -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('IpInfoFinder') +logger = Logger("IpInfoFinder") class IpInfoFinder: @@ -48,12 +44,12 @@ def get_info(target: str, debug: bool = False) -> dict: if debug: logger.setLevel(logging.DEBUG) - logger.info(f'Trying to get info by {target}') - ip_api_url = 'http://ip-api.com/json/' + logger.info(f"Trying to get info by {target}") + ip_api_url = "http://ip-api.com/json/" try: response = urllib.request.urlopen(ip_api_url + target) data = json.loads(response.read()) - logger.debug(f'Got info:\n {data}') + logger.debug(f"Got info:\n {data}") return data except URLError: - logger.raise_fatal(BaseException(f'Not valid IP or Domain: {target}')) + logger.raise_fatal(BaseException(f"Not valid IP or Domain: {target}")) diff --git a/src/logger/formatter.py b/src/logger/formatter.py index 60fc0f6..5f2dc14 100644 --- a/src/logger/formatter.py +++ b/src/logger/formatter.py @@ -15,17 +15,17 @@ class ConsoleFormatter(logging.Formatter): Console formatter. """ - info_color = '\x1b[0m' - warning_color = '\x1b[33;20m' - error_color = '\x1b[31;20m' - console_format = '%(asctime)s | %(levelname)-8s | %(name)s | %(message)s' + info_color = "\x1b[0m" + warning_color = "\x1b[33;20m" + error_color = "\x1b[31;20m" + console_format = "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s" FORMATS = { logging.INFO: info_color + console_format, logging.WARNING: warning_color + console_format + info_color, logging.DEBUG: info_color + console_format, logging.ERROR: error_color + console_format + info_color, - logging.CRITICAL: error_color + console_format + info_color + logging.CRITICAL: error_color + console_format + info_color, } def format(self, record): @@ -39,14 +39,14 @@ class FileFormatter(logging.Formatter): File formatter. """ - file_format = '%(asctime)s | %(levelname)-8s | %(name)s | %(message)s' + file_format = "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s" FORMATS = { logging.INFO: file_format, logging.WARNING: file_format, logging.DEBUG: file_format, logging.ERROR: file_format, - logging.CRITICAL: file_format + logging.CRITICAL: file_format, } def format(self, record): diff --git a/src/logger/logger.py b/src/logger/logger.py index 7b6d048..bca039a 100644 --- a/src/logger/logger.py +++ b/src/logger/logger.py @@ -7,9 +7,10 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import os import logging +import os from pathlib import Path + from .formatter import ConsoleFormatter, FileFormatter @@ -22,7 +23,7 @@ class Logger(logging.Logger): - file handler into file """ - def __init__(self, name: str, mode: str = 'a') -> None: + def __init__(self, name: str, mode: str = "a") -> None: """ Constructor. Logger level can be initializate by env variable `LOGGER_LEVEL`. @@ -34,17 +35,15 @@ def __init__(self, name: str, mode: str = 'a') -> None: logging.Logger.__init__(self, name) - self.setLevel(os.getenv('LOGGER_LEVEL', logging.INFO)) + self.setLevel(os.getenv("LOGGER_LEVEL", logging.INFO)) console_handler = logging.StreamHandler() console_handler.setLevel(self.level) console_handler.setFormatter(ConsoleFormatter()) - Path('./out/logs').mkdir(parents=True, exist_ok=True) + Path("./out/logs").mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler( - f'out/logs/{self.name}.log', - mode=mode, - encoding='utf-8' + f"out/logs/{self.name}.log", mode=mode, encoding="utf-8" ) file_handler.setLevel(self.level) file_handler.setFormatter(FileFormatter()) diff --git a/src/nmap_scanner/nmap_scanner.py b/src/nmap_scanner/nmap_scanner.py index ab00dfb..ad694eb 100644 --- a/src/nmap_scanner/nmap_scanner.py +++ b/src/nmap_scanner/nmap_scanner.py @@ -7,19 +7,16 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import logging +import sys + import nmap -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('NmapScanner') +logger = Logger("NmapScanner") class NmapScanner: @@ -29,9 +26,13 @@ class NmapScanner: """ @staticmethod - def nmap_scanner(target: str, arguments: str = '', - ports: str = '21-443', sudo: bool = False, - debug: bool = False) -> dict: + def nmap_scanner( + target: str, + arguments: str = "", + ports: str = "21-443", + sudo: bool = False, + debug: bool = False, + ) -> dict: """ Scans ports of the target. @@ -52,32 +53,36 @@ def nmap_scanner(target: str, arguments: str = '', logger.setLevel(logging.DEBUG) if not isinstance(target, str): - logger.raise_fatal(BaseException(f'Target must be a string not {type(target)}. ' - f'Got target: {target}')) + logger.raise_fatal( + BaseException( + f"Target must be a string not {type(target)}. " + f"Got target: {target}" + ) + ) try: nm_scan = nmap.PortScanner() nm_scan.scan(target.lower(), ports, arguments, sudo) - logger.debug(f'Command: {nm_scan.command_line()}') + logger.debug(f"Command: {nm_scan.command_line()}") ports_state = {} for host in nm_scan.all_hosts(): hostname = nm_scan[host].hostname() - logger.info(f'Host: {host} ({hostname})') - logger.info(f'State: {nm_scan[host].state()}') + logger.info(f"Host: {host} ({hostname})") + logger.info(f"State: {nm_scan[host].state()}") try: - logger.info('Scan all protocols') + logger.info("Scan all protocols") for proto in nm_scan[host].all_protocols(): - logger.info(f'Protocol: {proto}') + logger.info(f"Protocol: {proto}") lports = sorted(nm_scan[host][proto].keys()) except KeyError: - logger.raise_fatal(ValueError(f'Cannot scan {proto}')) + logger.raise_fatal(ValueError(f"Cannot scan {proto}")) for port in lports: state = nm_scan[host][proto][port]["state"] - logger.debug(f'Port: {port} State: {state}') + logger.debug(f"Port: {port} State: {state}") ports_state[port] = state return ports_state except Exception as ex: - logger.raise_fatal(BaseException(f'Error occurred: {ex}')) + logger.raise_fatal(BaseException(f"Error occurred: {ex}")) diff --git a/src/one_sec_mail/one_sec_mail.py b/src/one_sec_mail/one_sec_mail.py index 567d67f..a9d39bf 100644 --- a/src/one_sec_mail/one_sec_mail.py +++ b/src/one_sec_mail/one_sec_mail.py @@ -1,163 +1,164 @@ -""" - ██▄██ ▄▀▄ █▀▄ █▀▀ . █▀▄ █░█ - █░▀░█ █▄█ █░█ █▀▀ . █▀▄ ▀█▀ - ▀░░░▀ ▀░▀ ▀▀░ ▀▀▀ . ▀▀░ ░▀░ -▒▐█▀█─░▄█▀▄─▒▐▌▒▐▌░▐█▀▀▒██░░░░▐█▀█▄─░▄█▀▄─▒█▀█▀█ -▒▐█▄█░▐█▄▄▐█░▒█▒█░░▐█▀▀▒██░░░░▐█▌▐█░▐█▄▄▐█░░▒█░░ -▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ -""" - -import string -import random -from pathlib import Path -import requests - - -class OneSecMail: - """ - A class for creating one-time temporary mail. - It issues a temporary email address to which emails can be sent. - All emails are saved in the `mails/` folder (by default). - """ - - def __init__(self, - username: str = None, - username_length: int = 10, - mails_save_path: [str, Path] = 'mails', - save_attachments: bool = True) -> None: - """ - Constructor. - - Args: - * username - Username of the user (default: None). - * username_length - The length of the generated username - (default: 10). - * mails_save_path - Path to save messages (default: mails). - * save_attachments - Save attachments or not (default: True). - """ - - self.__api = 'https://www.1secmail.com/api/v1/' - self.__domain_list = ( - "1secmail.com", - "1secmail.org", - "1secmail.net" - ) - self.__domain = random.choice(self.__domain_list) - self.username = username - self.__username_length = username_length - self.__mails_save_path = Path(mails_save_path) - self.__save_attachments = save_attachments - self.__id_list = [] - - @property - def domain(self) -> str: - """ - Domain property method. - """ - return self.__domain - - def generate_username(self) -> str: - """ - Generates username. - Uses ascii lowercase letters and digits. - - Returns: - * The generated user name if the username was not specified, - otherwise the entered username. - """ - - if self.username is None: - print('[INFO] Username is not set. Generating...') - characters = string.ascii_lowercase + string.digits - username = ''.join(random.choice(characters) - for _ in range(self.__username_length)) - self.username = username - else: - print('[INFO] Username was set manually.') - print(f'[+] Your username: {self.username}.') - print(f'[+] Your email address: {self.username}@{self.__domain}.') - return self.username - - def check_mail(self) -> bool: - """ - Checks if there are new messages. - - Returns: - * True - if there are new messages, - False - otherwise. - """ - - login = f'login={self.username}&domain={self.__domain}' - req_url = f'{self.__api}?action=getMessages&{login}' - req = requests.get(req_url).json() - - if len(req) == 0: - print('[INFO] No new messages.') - return False - print(f'You have {len(req)} unread message(s).') - print('[INFO] Saving messages\' id.') - for mail in req: - for key, value in mail.items(): - if key == 'id': - self.__id_list.append(value) - return True - - def save_messages(self) -> None: - """ - Saves messages. - """ - - Path(self.__mails_save_path).mkdir(exist_ok=True, parents=True) - login = f'login={self.username}&domain={self.__domain}' - for i in self.__id_list: - read_msg = f'{self.__api}?action=readMessage&{login}&id={i}' - req = requests.get(read_msg).json() - - mail_file_path = Path(self.__mails_save_path) / f'{i}' / f'{i}.txt' - mail_file_path.parent.mkdir(exist_ok=True, parents=True) - with open(mail_file_path, 'w', encoding="utf-8") as file: - file.write(f'Sender: {req.get("from")}\n' + - f'Subject: {req.get("subject")}\n' + - f'To: {self.username}@{self.__domain}\n' + - f'Date: {req.get("date")}\n' + - f'Content: {req.get("textBody")}') - - if self.__save_attachments: - files_to_download = [] - attachments = req.get('attachments') - for attachment in attachments: - for key, value in attachment.items(): - if key == 'filename': - files_to_download.append(value) - if files_to_download: - print('[INFO] Saving attachments.') - with open(mail_file_path, 'a', encoding="utf-8") as file: - file.write('Attachments: ' + - f'{", ".join(files_to_download)}') - for file in files_to_download: - download_files = f'{self.__api}?action=download&' - download_files += f'{login}&id={i}&file={file}' - file_path = mail_file_path.parent / 'attachments' - file_path /= f'{file}' - file_path.parent.mkdir(exist_ok=True, parents=True) - response = requests.get(download_files) - with open(file_path, mode='wb') as file: - file.write(response.content) - else: - print('[INFO] No attachments found.') - - def delete_mailbox(self) -> None: - """ - Removes created mailbox. - """ - - url = 'https://www.1secmail.com/mailbox' - data = { - 'action': 'deleteMailbox', - 'login': self.username, - 'domain': self.__domain - } - - requests.post(url, data) - mailbox = f'{self.username}@{self.__domain}' - print(f'[X] Mailbox {mailbox} was removed.') +""" + ██▄██ ▄▀▄ █▀▄ █▀▀ . █▀▄ █░█ + █░▀░█ █▄█ █░█ █▀▀ . █▀▄ ▀█▀ + ▀░░░▀ ▀░▀ ▀▀░ ▀▀▀ . ▀▀░ ░▀░ +▒▐█▀█─░▄█▀▄─▒▐▌▒▐▌░▐█▀▀▒██░░░░▐█▀█▄─░▄█▀▄─▒█▀█▀█ +▒▐█▄█░▐█▄▄▐█░▒█▒█░░▐█▀▀▒██░░░░▐█▌▐█░▐█▄▄▐█░░▒█░░ +▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ +""" + +import random +import string +from pathlib import Path + +import requests + + +class OneSecMail: + """ + A class for creating one-time temporary mail. + It issues a temporary email address to which emails can be sent. + All emails are saved in the `mails/` folder (by default). + """ + + def __init__( + self, + username: str = None, + username_length: int = 10, + mails_save_path: [str, Path] = "mails", + save_attachments: bool = True, + ) -> None: + """ + Constructor. + + Args: + * username - Username of the user (default: None). + * username_length - The length of the generated username + (default: 10). + * mails_save_path - Path to save messages (default: mails). + * save_attachments - Save attachments or not (default: True). + """ + + self.__api = "https://www.1secmail.com/api/v1/" + self.__domain_list = ("1secmail.com", "1secmail.org", "1secmail.net") + self.__domain = random.choice(self.__domain_list) + self.username = username + self.__username_length = username_length + self.__mails_save_path = Path(mails_save_path) + self.__save_attachments = save_attachments + self.__id_list = [] + + @property + def domain(self) -> str: + """ + Domain property method. + """ + return self.__domain + + def generate_username(self) -> str: + """ + Generates username. + Uses ascii lowercase letters and digits. + + Returns: + * The generated user name if the username was not specified, + otherwise the entered username. + """ + + if self.username is None: + print("[INFO] Username is not set. Generating...") + characters = string.ascii_lowercase + string.digits + username = "".join( + random.choice(characters) for _ in range(self.__username_length) + ) + self.username = username + else: + print("[INFO] Username was set manually.") + print(f"[+] Your username: {self.username}.") + print(f"[+] Your email address: {self.username}@{self.__domain}.") + return self.username + + def check_mail(self) -> bool: + """ + Checks if there are new messages. + + Returns: + * True - if there are new messages, + False - otherwise. + """ + + login = f"login={self.username}&domain={self.__domain}" + req_url = f"{self.__api}?action=getMessages&{login}" + req = requests.get(req_url).json() + + if len(req) == 0: + print("[INFO] No new messages.") + return False + print(f"You have {len(req)} unread message(s).") + print("[INFO] Saving messages' id.") + for mail in req: + for key, value in mail.items(): + if key == "id": + self.__id_list.append(value) + return True + + def save_messages(self) -> None: + """ + Saves messages. + """ + + Path(self.__mails_save_path).mkdir(exist_ok=True, parents=True) + login = f"login={self.username}&domain={self.__domain}" + for i in self.__id_list: + read_msg = f"{self.__api}?action=readMessage&{login}&id={i}" + req = requests.get(read_msg).json() + + mail_file_path = Path(self.__mails_save_path) / f"{i}" / f"{i}.txt" + mail_file_path.parent.mkdir(exist_ok=True, parents=True) + with open(mail_file_path, "w", encoding="utf-8") as file: + file.write( + f'Sender: {req.get("from")}\n \ + Subject: {req.get("subject")}\n \ + To: {self.username}@{self.__domain}\n \ + Date: {req.get("date")}\n \ + Content: {req.get("textBody")}' + ) + + if self.__save_attachments: + files_to_download = [] + attachments = req.get("attachments") + for attachment in attachments: + for key, value in attachment.items(): + if key == "filename": + files_to_download.append(value) + if files_to_download: + print("[INFO] Saving attachments.") + with open(mail_file_path, "a", encoding="utf-8") as file: + file.write("Attachments: " + f'{", ".join(files_to_download)}') + for file in files_to_download: + download_files = f"{self.__api}?action=download&" + download_files += f"{login}&id={i}&file={file}" + file_path = mail_file_path.parent / "attachments" + file_path /= f"{file}" + file_path.parent.mkdir(exist_ok=True, parents=True) + response = requests.get(download_files) + with open(file_path, mode="wb") as file: + file.write(response.content) + else: + print("[INFO] No attachments found.") + + def delete_mailbox(self) -> None: + """ + Removes created mailbox. + """ + + url = "https://www.1secmail.com/mailbox" + data = { + "action": "deleteMailbox", + "login": self.username, + "domain": self.__domain, + } + + requests.post(url, data) + mailbox = f"{self.username}@{self.__domain}" + print(f"[X] Mailbox {mailbox} was removed.") diff --git a/src/phone_info/phone_info.py b/src/phone_info/phone_info.py index 0015548..96d6c44 100644 --- a/src/phone_info/phone_info.py +++ b/src/phone_info/phone_info.py @@ -7,19 +7,17 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import logging +import sys from pathlib import Path import folium import phonenumbers -from phonenumbers import geocoder, carrier +import requests from opencage.geocoder import OpenCageGeocode +from phonenumbers import carrier, geocoder -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger @@ -29,9 +27,7 @@ class PhoneInfo: Gets info by phone number. """ - def __init__(self, - number: str, - debug: bool = False) -> None: + def __init__(self, number: str, debug: bool = False) -> None: """ Constructor. @@ -65,9 +61,9 @@ def get_country(self) -> str: """ number = phonenumbers.parse(self.__number) - self.__logger.info('Find country') - country = geocoder.description_for_number(number, 'en') - self.__logger.debug(f'Country found: {country}') + self.__logger.info("Find country") + country = geocoder.description_for_number(number, "en") + self.__logger.debug(f"Country found: {country}") return country def get_operator(self) -> str: @@ -79,14 +75,12 @@ def get_operator(self) -> str: """ number = phonenumbers.parse(self.__number) - self.__logger.info('Find operator') - operator = carrier.name_for_number(number, 'en') - self.__logger.debug(f'Operator found: {operator}') + self.__logger.info("Find operator") + operator = carrier.name_for_number(number, "en") + self.__logger.debug(f"Operator found: {operator}") return operator - def draw_map(self, - api_key: str = None, - path_to_save: [str, Path] = None) -> None: + def draw_map(self, api_key: str = None, path_to_save: [str, Path] = None) -> None: """ Draws map with phone location. If api_key is not given - map will not be drawn. @@ -99,26 +93,122 @@ def draw_map(self, """ if api_key is None: - self.__logger.raise_fatal(ValueError('Api key not given')) + self.__logger.raise_fatal(ValueError("Api key not given")) geocode = OpenCageGeocode(api_key) location = self.get_country() results = geocode.geocode(location) - self.__logger.info('Get lat and lng') - lat = results[0]['geometry']['lat'] - lng = results[0]['geometry']['lng'] - self.__logger.debug(f'Lat: {lat}, Lng: {lng}') + self.__logger.info("Get lat and lng") + lat = results[0]["geometry"]["lat"] + lng = results[0]["geometry"]["lng"] + self.__logger.debug(f"Lat: {lat}, Lng: {lng}") my_map = folium.Map(location=[lat, lng], zoom_start=9) folium.Marker([lat, lng], popup=location).add_to(my_map) - self.__logger.info('Draw map') + self.__logger.info("Draw map") if path_to_save is None: - my_map.save(f'{self.__number}.html') - self.__logger.debug(f'Map was saved to {self.__number}.html') + my_map.save(f"{self.__number}.html") + self.__logger.debug(f"Map was saved to {self.__number}.html") else: Path(path_to_save).mkdir(exist_ok=True, parents=True) - my_map.save(f'{path_to_save}/{self.__number}.html') - self.__logger.debug(f'Map was saved to ' - f'{path_to_save}/{self.__number}.html') + my_map.save(f"{path_to_save}/{self.__number}.html") + self.__logger.debug( + f"Map was saved to " f"{path_to_save}/{self.__number}.html" + ) + + +class ProbivApiPhoneInfo: + """ + Gets info by phone number. + First of all, you need to register and get a subscription to this api: + https://probivapi.com/. + """ + + def __init__(self, secret_key: str, number: str, debug: bool = False) -> None: + """ + Constructor. + + Args: + * secret_key - Secret key from https://probivapi.com/ + * number - Phone number + * debug - Activate debug mode + """ + + self.__secret_key = secret_key + self.__number = number + self.__logger = Logger(self.__class__.__name__) + if debug: + self.__logger.setLevel(logging.DEBUG) + + @property + def number(self) -> str: + """ + Property number method. + """ + return self.__number + + @number.setter + def number(self, value): + self.__number = value + + def get_info_by_number(self) -> None: + """ + Gets all found info by phone number. + """ + + url = f"https://probivapi.com/api/phone/info/{self.__number}" + + head = {"X-Auth": self.__secret_key} + + response = requests.get(url, headers=head) + self.__logger.debug(response.text) + + try: + json_response = response.json() + except Exception: + json_response = {} + + callapp_data = json_response.get("callapp", {}) + callapp_api_name = callapp_data.get("name", "Not found") + callapp_emails = ", ".join( + [email.get("email") for email in callapp_data.get("emails", [])] + ) + callapp_websites = ", ".join( + [site.get("websiteUrl") for site in callapp_data.get("websites", [])] + ) + callapp_addresses = ", ".join( + [addr.get("street") for addr in callapp_data.get("addresses", [])] + ) + callapp_description = callapp_data.get("description", "Not found") + callapp_opening_hours = ", ".join( + [ + f"{day}: {', '.join(hours)}" + for day, hours in callapp_data.get("openingHours", {}).items() + ] + ) + callapp_lat = callapp_data.get("lat", "Not found") + callapp_lng = callapp_data.get("lng", "Not found") + callapp_spam_score = callapp_data.get("spamScore", "Not found") + callapp_priority = callapp_data.get("priority", "Not found") + eyecon_api_name = json_response.get("eyecon", "Not found") + viewcaller_name_list = [ + tag.get("name", "Not found") for tag in json_response.get("viewcaller", []) + ] + viewcaller_api_name = ", ".join(viewcaller_name_list) + + info = f"""\n✅ Info for {self.__number} +┣ 📱 ФИО (CallApp): {callapp_api_name} +┣ 📧 Emails (CallApp): {callapp_emails} +┣ 🌐 Сайты (CallApp): {callapp_websites} +┣ 🏠 Адреса (CallApp): {callapp_addresses} +┣ 📝 Описание (CallApp): {callapp_description} +┣ 🕒 Часы работы (CallApp): {callapp_opening_hours} +┣ 🌍 Координаты (CallApp): {callapp_lat}, {callapp_lng} +┣ ⚠️ Spam Score (CallApp): {callapp_spam_score} +┣ ⭐ Priority (CallApp): {callapp_priority} +┣ 🌐 ФИО (EyeCon): {eyecon_api_name} +┣ 🔎 ФИО (ViewCaller): {viewcaller_api_name}""" + + self.__logger.info(info) diff --git a/src/pwned/pwned.py b/src/pwned/pwned.py index cf4ba9f..9f9ea96 100644 --- a/src/pwned/pwned.py +++ b/src/pwned/pwned.py @@ -7,24 +7,19 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import hashlib import logging +import sys + import requests -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('pwned') -CHECK_URL = 'https://api.pwnedpasswords.com/range/' -HEADER = { - 'User-Agent': 'password checker' -} +logger = Logger("pwned") +CHECK_URL = "https://api.pwnedpasswords.com/range/" +HEADER = {"User-Agent": "password checker"} class PasswordPwned: @@ -48,25 +43,28 @@ def check_password(password: str, debug: bool = False) -> bool: if debug: logger.setLevel(logging.DEBUG) - logger.info('Calculating checksum for your password') - sha1 = hashlib.sha1(password.encode('utf-8')) + logger.info("Calculating checksum for your password") + sha1 = hashlib.sha1(password.encode("utf-8")) hash_string = sha1.hexdigest().upper() prefix = hash_string[0:5] - request = requests.get(CHECK_URL + prefix, headers=HEADER - ).content.decode('utf-8') - hashes = dict(t.split(':') for t in request.split('\r\n')) + request = requests.get(CHECK_URL + prefix, headers=HEADER).content.decode( + "utf-8" + ) + hashes = dict(t.split(":") for t in request.split("\r\n")) hashes = dict((prefix + key, value) for (key, value) in hashes.items()) - logger.info('Checking if your password exists in databases') + logger.info("Checking if your password exists in databases") for item_hash in hashes: if item_hash == hash_string: - logger.info('Pwned') - logger.debug(f'{password} has previously appeared in ' - f'a data breach, used {hashes[hash_string]} times, ' - 'and should never be used') + logger.info("Pwned") + logger.debug( + f"{password} has previously appeared in " + f"a data breach, used {hashes[hash_string]} times, " + "and should never be used" + ) return True - logger.info('No pwnage found') - logger.debug(f'{password} wasn\'t found in any of the Pwned Passwords') + logger.info("No pwnage found") + logger.debug(f"{password} wasn't found in any of the Pwned Passwords") return False diff --git a/src/robots_scanner/robots_scanner.py b/src/robots_scanner/robots_scanner.py index 16da3d1..aca35fa 100644 --- a/src/robots_scanner/robots_scanner.py +++ b/src/robots_scanner/robots_scanner.py @@ -7,20 +7,17 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ +import logging import re import sys -import logging + import requests -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") from logger.logger import Logger - -logger = Logger('RobotsScanner') +logger = Logger("RobotsScanner") class RobotsScanner: @@ -30,8 +27,9 @@ class RobotsScanner: This class will search for this file, parse it and return the result. """ - def __init__(self, target: str, accept_allow: bool = False, - debug: bool = False) -> None: + def __init__( + self, target: str, accept_allow: bool = False, debug: bool = False + ) -> None: """ Constructor. @@ -48,8 +46,12 @@ def __init__(self, target: str, accept_allow: bool = False, logger.setLevel(logging.DEBUG) if not isinstance(target, str): - logger.raise_fatal(BaseException(f'Target must be a string not {type(target)}. ' - f'Got target: {target}')) + logger.raise_fatal( + BaseException( + f"Target must be a string not {type(target)}. " + f"Got target: {target}" + ) + ) def __make_request(self) -> str: """ @@ -60,19 +62,22 @@ def __make_request(self) -> str: """ self.__target = self.__target.lower() - if not (self.__target.startswith('http://') or - self.__target.startswith('https://')): - self.__target = 'http://' + self.__target + if not ( + self.__target.startswith("http://") or self.__target.startswith("https://") + ): + self.__target = "http://" + self.__target self.__target = ( - self.__target + '/' - ) if not self.__target.endswith('/') else self.__target + (self.__target + "/") if not self.__target.endswith("/") else self.__target + ) self.__target = ( - self.__target + 'robots.txt' - ) if not self.__target.endswith('robots.txt') else self.__target + (self.__target + "robots.txt") + if not self.__target.endswith("robots.txt") + else self.__target + ) - logger.info(f'Robots scanner for {self.__target}') + logger.info(f"Robots scanner for {self.__target}") return requests.get(self.__target).text @@ -88,12 +93,10 @@ def __check_valid_line(self, line: str) -> bool: """ regex_pattern = ( - r"^((dis)?allow|user)" if self.__accept_allow - else r"^(disallow|user)" + r"^((dis)?allow|user)" if self.__accept_allow else r"^(disallow|user)" ) - return line and bool(re.match(regex_pattern, line.strip(), - flags=re.IGNORECASE)) + return line and bool(re.match(regex_pattern, line.strip(), flags=re.IGNORECASE)) def parse_lines(self) -> dict: """ @@ -106,17 +109,17 @@ def parse_lines(self) -> dict: robots_text = self.__make_request() logger.debug(robots_text) - logger.info('Checking if each line satisfies the conditions') - lines = [line for line in robots_text.splitlines() - if self.__check_valid_line(line)] + logger.info("Checking if each line satisfies the conditions") + lines = [ + line for line in robots_text.splitlines() if self.__check_valid_line(line) + ] data_dict = {} user_agent = None - logger.info('Parsing output lines') + logger.info("Parsing output lines") for line in lines: if "user agent" in line.lower(): - line = re.sub(r"user\sagent:", line, "user-agent", - flags=re.IGNORECASE) + line = re.sub(r"user\sagent:", line, "user-agent", flags=re.IGNORECASE) rule, *values = filter(None, re.split(r"\s+|\t+", line.strip())) rule = rule or "" value = values[0] if len(values) else "" @@ -134,8 +137,9 @@ def parse_lines(self) -> dict: return {k: v for k, v in data_dict.items() if v} @staticmethod - def robots_scanner(target: str, accept_allow: bool = False, - debug: bool = False) -> dict: + def robots_scanner( + target: str, accept_allow: bool = False, debug: bool = False + ) -> dict: """ Method for searching robots.txt file, parsing it and returning the result. @@ -152,4 +156,4 @@ def robots_scanner(target: str, accept_allow: bool = False, robots = RobotsScanner(target, accept_allow, debug) return robots.parse_lines() except Exception as ex: - logger.raise_fatal(BaseException(f'Error occurred: {ex}')) + logger.raise_fatal(BaseException(f"Error occurred: {ex}")) diff --git a/src/whois_lookup/whois_lookup.py b/src/whois_lookup/whois_lookup.py index ccdfad5..8dd75fa 100644 --- a/src/whois_lookup/whois_lookup.py +++ b/src/whois_lookup/whois_lookup.py @@ -7,19 +7,15 @@ ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ """ -import sys import logging +import sys -sys.path.insert( - 0, - 'src' -) +sys.path.insert(0, "src") -from logger.logger import Logger from exec_shell_command.exec_shell_command import exec_shell_command +from logger.logger import Logger - -logger = Logger('WhoisLookup') +logger = Logger("WhoisLookup") class WhoisLookup: @@ -50,15 +46,19 @@ def whois_lookup(target: str, debug: bool = False) -> str: logger.setLevel(logging.DEBUG) if not isinstance(target, str): - logger.raise_fatal(BaseException(f'Target must be a string not {type(target)}. ' - f'Got target: {target}')) + logger.raise_fatal( + BaseException( + f"Target must be a string not {type(target)}. " + f"Got target: {target}" + ) + ) - logger.info('Cleanup console') + logger.info("Cleanup console") try: - exec_shell_command('reset') - command = f'whois {target.lower()}' + exec_shell_command("reset") + command = f"whois {target.lower()}" result = exec_shell_command(command) - logger.debug(f'{command} output: {result}') + logger.debug(f"{command} output: {result}") return result except Exception as ex: - logger.raise_fatal(BaseException(f'Error occurred: {ex}')) + logger.raise_fatal(BaseException(f"Error occurred: {ex}"))