├── config.ini ├── requirements.txt ├── images ├── miz.ico ├── mizogg-eyes.png └── mizogglogo.png ├── input ├── 130.txt ├── 135.txt ├── 140.txt ├── 145.txt ├── 150.txt ├── 155.txt ├── 160.txt ├── pubkey_1_50_test.txt ├── 130-160.txt └── btc.txt ├── keyhunt ├── keyhunt ├── cygwin1.dll ├── keyhunt.exe ├── cygstdc++-6.dll └── cyggcc_s-seh-1.dll ├── libs ├── progress_dialog.py ├── command_thread.py ├── console_gui.py ├── about_dialog.py ├── Range_gui.py └── theme_manager.py ├── README.md ├── main.py └── tkmain.py /config.ini: -------------------------------------------------------------------------------- 1 | [theme] 2 | name = cyberpunk 3 | 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt6>=6.4.0 2 | configparser>=5.3.0 -------------------------------------------------------------------------------- /images/miz.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/images/miz.ico -------------------------------------------------------------------------------- /input/130.txt: -------------------------------------------------------------------------------- 1 | 03633cbe3ec02b9401c5effa144c5b4d22f87940259634858fc7e59b1c09937852 -------------------------------------------------------------------------------- /input/135.txt: -------------------------------------------------------------------------------- 1 | 02145d2611c823a396ef6712ce0f712f09b9b4f3135e3e0aa3230fb9b6d08d1e16 -------------------------------------------------------------------------------- /input/140.txt: -------------------------------------------------------------------------------- 1 | 031f6a332d3c5c4f2de2378c012f429cd109ba07d69690c6c701b6bb87860d6640 -------------------------------------------------------------------------------- /input/145.txt: -------------------------------------------------------------------------------- 1 | 03afdda497369e219a2c1c369954a930e4d3740968e5e4352475bcffce3140dae5 -------------------------------------------------------------------------------- /input/150.txt: -------------------------------------------------------------------------------- 1 | 03137807790ea7dc6e97901c2bc87411f45ed74a5629315c4e4b03a0a102250c49 -------------------------------------------------------------------------------- /input/155.txt: -------------------------------------------------------------------------------- 1 | 035cd1854cae45391ca4ec428cc7e6c7d9984424b954209a8eea197b9e364c05f6 -------------------------------------------------------------------------------- /input/160.txt: -------------------------------------------------------------------------------- 1 | 02e0a8b039282faf6fe0fd769cfbc4b6b4cf8758ba68220eac420e32b91ddfa673 -------------------------------------------------------------------------------- /keyhunt/keyhunt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/keyhunt/keyhunt -------------------------------------------------------------------------------- /keyhunt/cygwin1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/keyhunt/cygwin1.dll -------------------------------------------------------------------------------- /keyhunt/keyhunt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/keyhunt/keyhunt.exe -------------------------------------------------------------------------------- /images/mizogg-eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/images/mizogg-eyes.png -------------------------------------------------------------------------------- /images/mizogglogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/images/mizogglogo.png -------------------------------------------------------------------------------- /keyhunt/cygstdc++-6.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/keyhunt/cygstdc++-6.dll -------------------------------------------------------------------------------- /keyhunt/cyggcc_s-seh-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mizogg/GUI_keyhunt/HEAD/keyhunt/cyggcc_s-seh-1.dll -------------------------------------------------------------------------------- /input/pubkey_1_50_test.txt: -------------------------------------------------------------------------------- 1 | 03a7a4c30291ac1db24b4ab00c442aa832f7794b5a0959bec6e8d7fee802289dcd 2 | 033c4a45cbd643ff97d77f41ea37e843648d50fd894b864b0d52febc62f6454f7c 3 | 030d282cf2ff536d2c42f105d0b8588821a915dc3f9a05bd98bb23af67a2e92a5b 4 | 03a2efa402fd5268400c77c20e574ba86409ededee7c4020e4b9f0edbee53de0d4 5 | 03f46f41027bbf44fafd6b059091b900dad41e6845b2241dc3254c7cdd3c5a16c6 -------------------------------------------------------------------------------- /input/130-160.txt: -------------------------------------------------------------------------------- 1 | 03633cbe3ec02b9401c5effa144c5b4d22f87940259634858fc7e59b1c09937852 2 | 02145d2611c823a396ef6712ce0f712f09b9b4f3135e3e0aa3230fb9b6d08d1e16 3 | 031f6a332d3c5c4f2de2378c012f429cd109ba07d69690c6c701b6bb87860d6640 4 | 03afdda497369e219a2c1c369954a930e4d3740968e5e4352475bcffce3140dae5 5 | 03137807790ea7dc6e97901c2bc87411f45ed74a5629315c4e4b03a0a102250c49 6 | 035cd1854cae45391ca4ec428cc7e6c7d9984424b954209a8eea197b9e364c05f6 7 | 02e0a8b039282faf6fe0fd769cfbc4b6b4cf8758ba68220eac420e32b91ddfa673 8 | -------------------------------------------------------------------------------- /libs/progress_dialog.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Team Mizogg 3 | """ 4 | from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLabel, QWidget, QHBoxLayout, QPushButton 5 | from PyQt6.QtGui import QIcon 6 | 7 | ICO_ICON = "images/miz.ico" 8 | 9 | class ProgressDialog(QDialog): 10 | def __init__(self, parent=None): 11 | super().__init__(parent) 12 | self.setWindowTitle("File Found") 13 | self.setWindowIcon(QIcon(f"{ICO_ICON}")) 14 | 15 | layout = QVBoxLayout(self) 16 | message_label = QLabel("Progress file found. Do you want to delete it?", self) 17 | layout.addWidget(message_label) 18 | 19 | button_widget = QWidget(self) 20 | button_layout = QHBoxLayout(button_widget) 21 | 22 | self.keep_button = QPushButton("Keep", self) 23 | self.keep_button.setStyleSheet( 24 | "QPushButton { font-size: 12pt; }" 25 | "QPushButton:hover { font-size: 12pt; }" 26 | ) 27 | self.keep_button.clicked.connect(self.keep_clicked) 28 | button_layout.addWidget(self.keep_button) 29 | 30 | self.delete_button = QPushButton("Delete", self) 31 | self.delete_button.setStyleSheet( 32 | "QPushButton { font-size: 12pt; }" 33 | "QPushButton:hover { font-size: 12pt; }" 34 | ) 35 | self.delete_button.clicked.connect(self.delete_clicked) 36 | button_layout.addWidget(self.delete_button) 37 | 38 | layout.addWidget(button_widget) 39 | 40 | def keep_clicked(self): 41 | self.done(QDialog.DialogCode.Rejected) 42 | 43 | def delete_clicked(self): 44 | self.done(QDialog.DialogCode.Accepted) 45 | -------------------------------------------------------------------------------- /libs/command_thread.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Team Mizogg 3 | """ 4 | import subprocess 5 | import platform 6 | from PyQt6.QtCore import QThread, pyqtSignal 7 | 8 | class CommandThread(QThread): 9 | commandOutput = pyqtSignal(str) 10 | commandFinished = pyqtSignal(int) 11 | 12 | def __init__(self, command): 13 | super().__init__() 14 | self.command = command 15 | self.process = None 16 | 17 | def run(self): 18 | # Convert command list to string for display 19 | if isinstance(self.command, list): 20 | command_str = ' '.join(str(x) for x in self.command) 21 | else: 22 | command_str = str(self.command) 23 | 24 | # Emit the initial command to indicate starting 25 | self.commandOutput.emit(f"Executing command: {command_str}") 26 | 27 | try: 28 | if platform.system() == "Windows": 29 | startupinfo = subprocess.STARTUPINFO() 30 | startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 31 | self.process = subprocess.Popen( 32 | self.command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', startupinfo=startupinfo 33 | ) 34 | elif platform.system() == "Linux": 35 | self.process = subprocess.Popen( 36 | self.command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8' 37 | ) 38 | else: 39 | raise NotImplementedError(f"Unsupported platform: {platform.system()}") 40 | 41 | # Read command output line by line and emit each line 42 | for line in iter(self.process.stdout.readline, ''): 43 | output = line.rstrip('\n') 44 | self.commandOutput.emit(output) 45 | 46 | # Close the stdout stream and wait for the process to complete 47 | self.process.stdout.close() 48 | self.commandFinished.emit(self.process.wait()) 49 | 50 | except Exception as e: 51 | # Emit any exceptions that occur during command execution 52 | self.commandOutput.emit(f"Error: {str(e)}") 53 | self.commandFinished.emit(-1) # Use a custom error code or handle differently 54 | 55 | finally: 56 | if self.process and self.process.poll() is None: 57 | self.process.kill() # Ensure process termination if not already done 58 | 59 | def stop(self): 60 | if self.process and self.process.poll() is None: 61 | self.process.kill() # Terminate the process if running 62 | -------------------------------------------------------------------------------- /libs/console_gui.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Team Mizogg 3 | """ 4 | from PyQt6.QtCore import QMetaObject, Qt, Q_ARG 5 | from PyQt6.QtWidgets import * 6 | from PyQt6.QtCore import pyqtSlot 7 | 8 | class ConsoleWindow(QWidget): 9 | def __init__(self, parent=None): 10 | super().__init__(parent) 11 | self.layout = QVBoxLayout(self) 12 | self.consoleOutput = QPlainTextEdit(self) 13 | self.consoleOutput.setReadOnly(True) 14 | self.layout.addWidget(self.consoleOutput) 15 | 16 | button_widget = QWidget(self) 17 | button_layout = QHBoxLayout(button_widget) 18 | 19 | self.clearButton = QPushButton("Clear", self) 20 | self.clearButton.setStyleSheet( 21 | "QPushButton { font-size: 12pt; }" 22 | "QPushButton:hover { font-size: 12pt; }" 23 | ) 24 | button_layout.addWidget(self.clearButton) 25 | 26 | self.selectAllButton = QPushButton("Select All", self) 27 | self.selectAllButton.setStyleSheet( 28 | "QPushButton { font-size: 12pt; }" 29 | "QPushButton:hover { font-size: 12pt; }" 30 | ) 31 | button_layout.addWidget(self.selectAllButton) 32 | 33 | self.copyButton = QPushButton("Copy", self) 34 | self.copyButton.setStyleSheet( 35 | "QPushButton { font-size: 12pt; }" 36 | "QPushButton:hover { font-size: 12pt; }" 37 | ) 38 | button_layout.addWidget(self.copyButton) 39 | 40 | self.thresholdLabel = QLabel("Console Threshold:", self) 41 | self.thresholdDropdown = QComboBox(self) 42 | self.thresholdDropdown.addItems(["50", "100", "500", "1000"]) 43 | self.thresholdDropdown.setCurrentIndex(2) 44 | button_layout.addWidget(self.thresholdLabel) 45 | button_layout.addWidget(self.thresholdDropdown) 46 | self.layout.addWidget(button_widget) 47 | 48 | self.clearButton.clicked.connect(self.clear_console) 49 | self.selectAllButton.clicked.connect(self.select_all) 50 | self.copyButton.clicked.connect(self.copy_text) 51 | self.thresholdDropdown.currentIndexChanged.connect(self.update_threshold) 52 | 53 | self.threshold = int(self.thresholdDropdown.currentText()) 54 | 55 | def set_output(self, output): 56 | self.consoleOutput.setPlainText(output) 57 | 58 | def append_output(self, output): 59 | QMetaObject.invokeMethod(self.consoleOutput, "appendPlainText", Qt.ConnectionType.QueuedConnection, Q_ARG(str, output)) 60 | line_count = self.consoleOutput.document().blockCount() 61 | if line_count > self.threshold: 62 | QMetaObject.invokeMethod(self.consoleOutput, "clear", Qt.ConnectionType.QueuedConnection) 63 | 64 | @pyqtSlot() 65 | def clear_console(self): 66 | self.consoleOutput.clear() 67 | 68 | @pyqtSlot() 69 | def select_all(self): 70 | self.consoleOutput.selectAll() 71 | 72 | @pyqtSlot() 73 | def copy_text(self): 74 | cursor = self.consoleOutput.textCursor() 75 | selected_text = cursor.selectedText() 76 | clipboard = QApplication.clipboard() 77 | clipboard.setText(selected_text) 78 | 79 | @pyqtSlot() 80 | def update_threshold(self): 81 | self.threshold = int(self.thresholdDropdown.currentText()) 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KeyHunter Puzzles GUI 2 | 3 | ![photo_2025-05-26_16-27-18](https://github.com/user-attachments/assets/c0ad7c30-d4fc-45fc-8c52-8377645bf7dd) 4 | 5 | Advanced Bitcoin and Ethereum Key Hunting Tool with both PyQt6 and Tkinter GUI interfaces. 6 | 7 | A powerful GUI application for hunting private keys in the Bitcoin blockchain, featuring multi-instance support, theme customization, and advanced range management tools. 8 | 9 | ### GUI Versions 10 | 11 | #### PyQt6 Version (main.py) 12 | - Modern Qt-based interface 13 | - Advanced styling and theming 14 | - Rich text console output 15 | - Multi-window support 16 | 17 | #### Tkinter Version (tkmain.py) 18 | - Lightweight native interface 19 | - Multiple theme options 20 | - Custom color schemes 21 | - Cross-platform compatibility 22 | 23 | ![image](https://github.com/user-attachments/assets/ec1c3a65-ad6b-4544-ab60-8d4cf5576d91) 24 | 25 | ## Features 26 | 27 | ### Core Functionality 28 | - Multiple scanning modes: address, bsgs, and rmd160 29 | - Support for both BTC and ETH cryptocurrencies 30 | - Configurable CPU usage per instance 31 | - Advanced range management and splitting 32 | - Progress tracking and file management 33 | 34 | ### Multi-Instance Support 35 | - Run multiple instances simultaneously (1, 2, 4, 6, or 8 instances) 36 | - Automatic CPU distribution across instances 37 | - Each instance has its own console window for output 38 | - Shared configuration across all instances 39 | - Single start/stop control for all instances 40 | 41 | ### Theme Support 42 | - Light and dark theme options 43 | - Theme settings persist between sessions 44 | - Consistent styling across all components 45 | - Customizable through the Settings menu 46 | 47 | ### Range Management Tools 48 | - Hexadecimal range calculator 49 | - Range splitting into equal parts 50 | - Percentage-based range calculations 51 | - Visual range slider (1-256 bits) 52 | - Support for both compressed and uncompressed keys 53 | 54 | ### File Management 55 | - Input file browser 56 | - Progress file management 57 | - Automatic key found detection 58 | - File format validation 59 | 60 | ### User Interface 61 | - Modern, responsive design 62 | - Clear console output 63 | - Configurable console threshold 64 | - Copy and clear console functions 65 | - Tooltips with helpful information 66 | 67 | ## Configuration 68 | 69 | ### CPU Settings 70 | - Automatic CPU count detection 71 | - CPU allocation based on number of instances 72 | - Example: With 8 CPUs and 8 instances, each instance gets 1 CPU 73 | - Example: With 8 CPUs and 4 instances, each instance can use up to 2 CPUs 74 | 75 | ### Range Settings 76 | - Default range: 400000000000000000:7FFFFFFFFFFFFFFFFF (71 bits) 77 | - Adjustable through slider or direct input 78 | - Range splitting for multi-instance operation 79 | - Support for custom hex ranges 80 | 81 | ### Mode Settings 82 | - Address mode: Search for specific addresses 83 | - BSGS mode: Baby-Step Giant-Step algorithm 84 | - RMD160 mode: RIPEMD-160 hash search 85 | - Configurable movement modes (sequential, random, etc.) 86 | 87 | ## Usage 88 | 89 | 1. Select the number of instances from the Instances menu 90 | 2. Configure your scanning parameters: 91 | - Choose mode (address, bsgs, rmd160) 92 | - Select cryptocurrency (BTC/ETH) 93 | - Set CPU count per instance 94 | - Configure range and other options 95 | 3. Click "Start All Instances" to begin scanning 96 | 4. Monitor progress in individual console windows 97 | 5. Use "Stop All Instances" to halt scanning 98 | 99 | ## Theme Customization 100 | 101 | Access theme settings through: 102 | 1. File menu -> Settings 103 | 2. Choose between light and dark themes 104 | 3. Settings are saved in config.ini 105 | 4. Changes apply immediately 106 | 107 | ## Requirements 108 | 109 | - Python 3.6 or higher 110 | - PyQt6 111 | - Windows/Linux operating system 112 | - Sufficient RAM for selected k-value: 113 | - 2 GB RAM: -k 128 114 | - 4 GB RAM: -k 256 115 | - 8 GB RAM: -k 512 116 | - 16 GB RAM: -k 1024 117 | - 32 GB RAM: -k 2048 118 | 119 | ## Installation 120 | 121 | ### Requirements 122 | ``` 123 | Python 3.8 or higher 124 | PyQt6 (for main.py) 125 | tkinter (usually comes with Python) 126 | configparser 127 | ``` 128 | 129 | ### Install Dependencies 130 | ```bash 131 | pip install -r requirements.txt 132 | ``` 133 | 134 | ## Usage 135 | 136 | ### Running the Application 137 | 138 | #### PyQt6 Version 139 | ```bash 140 | python main.py 141 | ``` 142 | 143 | #### Tkinter Version 144 | ```bash 145 | python tkmain.py 146 | ``` 147 | 148 | ## Support 149 | 150 | For support and updates: 151 | - Visit: https://mizogg.com 152 | - Visit: https://mizogg.co.uk 153 | - Telegram: https://t.me/TeamHunter_GUI 154 | 155 | ## Credits 156 | 157 | Made by Team Mizogg 158 | © mizogg.com 2018 - 2025 159 | -------------------------------------------------------------------------------- /libs/about_dialog.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Team Mizogg 3 | """ 4 | from PyQt6.QtWidgets import QDialog, QVBoxLayout, QLabel, QTextBrowser, QPushButton 5 | from PyQt6.QtGui import QIcon, QPixmap 6 | from PyQt6.QtCore import QSize, Qt 7 | import webbrowser 8 | import platform 9 | 10 | ICO_ICON = "images/miz.ico" 11 | TITLE_ICON = "images/mizogglogo.png" 12 | RED_ICON = "images/mizogg-eyes.png" 13 | version = '1.2' 14 | 15 | def open_website(): 16 | webbrowser.open("https://mizogg.co.uk") 17 | 18 | class AboutDialog(QDialog): 19 | def __init__(self, parent=None): 20 | super().__init__(parent) 21 | self.setWindowTitle("About KeyHunter Puzzles GUI") 22 | self.setWindowIcon(QIcon(ICO_ICON)) 23 | self.setMinimumSize(800, 600) 24 | self.setStyleSheet("font-size: 14px; font-weight: bold;") 25 | 26 | layout = QVBoxLayout() 27 | layout.setAlignment(Qt.AlignmentFlag.AlignTop) 28 | 29 | # Header with logo 30 | header_pixmap = QPixmap(TITLE_ICON) 31 | header_label = QLabel() 32 | header_label.setPixmap(header_pixmap) 33 | layout.addWidget(header_label, alignment=Qt.AlignmentFlag.AlignCenter) 34 | layout.addSpacing(20) 35 | 36 | # Version and Platform Info 37 | current_platform = platform.system() 38 | info_label = QLabel(f"KeyHunter Puzzles GUI v{version} ({current_platform})\nMade by Team Mizogg") 39 | info_label.setAlignment(Qt.AlignmentFlag.AlignCenter) 40 | layout.addWidget(info_label) 41 | layout.addSpacing(20) 42 | 43 | # Main Content 44 | content_browser = QTextBrowser() 45 | content_browser.setOpenExternalLinks(True) 46 | content_browser.setHtml(""" 47 |

🔍 KeyHunter Puzzles GUI - Advanced Bitcoin Key Hunting Tool 🔍

48 | 49 |

🌟 Major Features:

50 | 51 |

1. Multi-Instance Support

52 | • Run up to 8 instances simultaneously
53 | • Each instance gets its own console window
54 | • Automatic CPU distribution
55 | • Shared configuration across all instances
56 | • Single start/stop control for all instances

57 | 58 |

2. Theme Customization

59 | • Light and dark theme options
60 | • Settings persist between sessions
61 | • Consistent styling across all components
62 | • Easy access through Settings menu

63 | 64 |

3. Advanced Range Management

65 | • Hexadecimal range calculator
66 | • Automatic range splitting for multiple instances
67 | • Percentage-based calculations
68 | • Visual range slider (1-256 bits)
69 | • Support for both compressed and uncompressed keys

70 | 71 |

4. Enhanced Scanning Capabilities

72 | • Multiple modes: address, bsgs, and rmd160
73 | • Support for both BTC and ETH
74 | • Configurable CPU usage per instance
75 | • Advanced progress tracking
76 | • File management tools

77 | 78 |

5. User-Friendly Interface

79 | • Modern, responsive design
80 | • Clear console output
81 | • Configurable console threshold
82 | • Copy and clear console functions
83 | • Helpful tooltips throughout

84 | 85 |

💻 System Requirements:

86 | • Python 3.6 or higher
87 | • PyQt6
88 | • Windows/Linux OS
89 | • RAM recommendations:
90 | - 2 GB: -k 128
91 | - 4 GB: -k 256
92 | - 8 GB: -k 512
93 | - 16 GB: -k 1024
94 | - 32 GB: -k 2048

95 | 96 |

🔧 CPU Distribution Examples:

97 | • 8 CPUs, 8 instances = 1 CPU per instance
98 | • 8 CPUs, 4 instances = 2 CPUs per instance
99 | • Automatic optimization for your system

100 | 101 |

📊 Default Range:

102 | • 400000000000000000:7FFFFFFFFFFFFFFFFF (71 bits)
103 | • Fully adjustable through slider or direct input
104 | • Automatic splitting for multi-instance operation

105 | 106 |

🚀 Quick Start:

107 | 1. Select instances (1-8) from menu
108 | 2. Configure scanning parameters:
109 | - Choose mode (address, bsgs, rmd160)
110 | - Select cryptocurrency (BTC/ETH)
111 | - Set CPU count per instance
112 | - Configure range and other options
113 | 3. Click "Start All Instances"
114 | 4. Monitor progress in console windows
115 | 5. Use "Stop All Instances" when needed

116 | 117 |

🎯 Scanning Modes:

118 | • Address mode: Search specific addresses
119 | • BSGS mode: Baby-Step Giant-Step algorithm
120 | • RMD160 mode: RIPEMD-160 hash search
121 | • Multiple movement modes (sequential, random, etc.)

122 | 123 |

💡 Pro Tips:

124 | • Use the range calculator for precise searches
125 | • Monitor CPU usage across instances
126 | • Save your configurations for future use
127 | • Check the tooltips for helpful information

128 | 129 |

📞 Support:

130 | • Website: https://mizogg.co.uk
131 | • Telegram: https://t.me/TeamHunter_GUI

132 | 133 |

134 | Made by Team Mizogg
135 | © mizogg.com 2018 - 2025 136 |

137 | """) 138 | layout.addWidget(content_browser) 139 | 140 | # Website Button 141 | icon_size = QSize(26, 26) 142 | iconred = QIcon(QPixmap(RED_ICON)) 143 | self.miz_git_mode_button = QPushButton(self) 144 | self.miz_git_mode_button.setToolTip('Help ME. Just by visiting my site https://mizogg.co.uk keep up those clicks. Mizogg Website and Information ') 145 | self.miz_git_mode_button.setStyleSheet("font-size: 12pt;") 146 | self.miz_git_mode_button.setIconSize(icon_size) 147 | self.miz_git_mode_button.setIcon(iconred) 148 | self.miz_git_mode_button.clicked.connect(open_website) 149 | layout.addWidget(self.miz_git_mode_button) 150 | 151 | self.setLayout(layout) -------------------------------------------------------------------------------- /input/btc.txt: -------------------------------------------------------------------------------- 1 | 1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH 2 | 1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb 3 | 19ZewH8Kk1PDbSNdJ97FP4EiCjTRaZMZQA 4 | 1EhqbyUMvvs7BfL8goY6qcPbD6YKfPqb7e 5 | 1E6NuFjCi27W5zoXg8TRdcSRq84zJeBW3k 6 | 1PitScNLyp2HCygzadCh7FveTnfmpPbfp8 7 | 1McVt1vMtCC7yn5b9wgX1833yCcLXzueeC 8 | 1M92tSqNmQLYw33fuBvjmeadirh1ysMBxK 9 | 1CQFwcjw1dwhtkVWBttNLDtqL7ivBonGPV 10 | 1LeBZP5QCwwgXRtmVUvTVrraqPUokyLHqe 11 | 1PgQVLmst3Z314JrQn5TNiys8Hc38TcXJu 12 | 1DBaumZxUkM4qMQRt2LVWyFJq5kDtSZQot 13 | 1Pie8JkxBT6MGPz9Nvi3fsPkr2D8q3GBc1 14 | 1ErZWg5cFCe4Vw5BzgfzB74VNLaXEiEkhk 15 | 1QCbW9HWnwQWiQqVo5exhAnmfqKRrCRsvW 16 | 1BDyrQ6WoF8VN3g9SAS1iKZcPzFfnDVieY 17 | 1HduPEXZRdG26SUT5Yk83mLkPyjnZuJ7Bm 18 | 1GnNTmTVLZiqQfLbAdp9DVdicEnB5GoERE 19 | 1NWmZRpHH4XSPwsW6dsS3nrNWfL1yrJj4w 20 | 1HsMJxNiV7TLxmoF6uJNkydxPFDog4NQum 21 | 14oFNXucftsHiUMY8uctg6N487riuyXs4h 22 | 1CfZWK1QTQE3eS9qn61dQjV89KDjZzfNcv 23 | 1L2GM8eE7mJWLdo3HZS6su1832NX2txaac 24 | 1rSnXMr63jdCuegJFuidJqWxUPV7AtUf7 25 | 15JhYXn6Mx3oF4Y7PcTAv2wVVAuCFFQNiP 26 | 1JVnST957hGztonaWK6FougdtjxzHzRMMg 27 | 128z5d7nN7PkCuX5qoA4Ys6pmxUYnEy86k 28 | 12jbtzBb54r97TCwW3G1gCFoumpckRAPdY 29 | 19EEC52krRUK1RkUAEZmQdjTyHT7Gp1TYT 30 | 1LHtnpd8nU5VHEMkG2TMYYNUjjLc992bps 31 | 1LhE6sCTuGae42Axu1L1ZB7L96yi9irEBE 32 | 1FRoHA9xewq7DjrZ1psWJVeTer8gHRqEvR 33 | 187swFMjz1G54ycVU56B7jZFHFTNVQFDiu 34 | 1PWABE7oUahG2AFFQhhvViQovnCr4rEv7Q 35 | 1PWCx5fovoEaoBowAvF5k91m2Xat9bMgwb 36 | 1Be2UF9NLfyLFbtm3TCbmuocc9N1Kduci1 37 | 14iXhn8bGajVWegZHJ18vJLHhntcpL4dex 38 | 1HBtApAFA9B2YZw3G2YKSMCtb3dVnjuNe2 39 | 122AJhKLEfkFBaGAd84pLp1kfE7xK3GdT8 40 | 1EeAxcprB2PpCnr34VfZdFrkUWuxyiNEFv 41 | 1L5sU9qvJeuwQUdt4y1eiLmquFxKjtHr3E 42 | 1E32GPWgDyeyQac4aJxm9HVoLrrEYPnM4N 43 | 1PiFuqGpG8yGM5v6rNHWS3TjsG6awgEGA1 44 | 1CkR2uS7LmFwc3T2jV8C1BhWb5mQaoxedF 45 | 1NtiLNGegHWE3Mp9g2JPkgx6wUg4TW7bbk 46 | 1F3JRMWudBaj48EhwcHDdpeuy2jwACNxjP 47 | 1Pd8VvT49sHKsmqrQiP61RsVwmXCZ6ay7Z 48 | 1DFYhaB2J9q1LLZJWKTnscPWos9VBqDHzv 49 | 12CiUhYVTTH33w3SPUBqcpMoqnApAV4WCF 50 | 1MEzite4ReNuWaL5Ds17ePKt2dCxWEofwk 51 | 1NpnQyZ7x24ud82b7WiRNvPm6N8bqGQnaS 52 | 15z9c9sVpu6fwNiK7dMAFgMYSK4GqsGZim 53 | 15K1YKJMiJ4fpesTVUcByoz334rHmknxmT 54 | 1KYUv7nSvXx4642TKeuC2SNdTk326uUpFy 55 | 1LzhS3k3e9Ub8i2W1V8xQFdB8n2MYCHPCa 56 | 17aPYR1m6pVAacXg1PTDDU7XafvK1dxvhi 57 | 15c9mPGLku1HuW9LRtBf4jcHVpBUt8txKz 58 | 1Dn8NF8qDyyfHMktmuoQLGyjWmZXgvosXf 59 | 1HAX2n9Uruu9YDt4cqRgYcvtGvZj1rbUyt 60 | 1Kn5h2qpgw9mWE5jKpk8PP4qvvJ1QVy8su 61 | 1AVJKwzs9AskraJLGHAZPiaZcrpDr1U6AB 62 | 1Me6EfpwZK5kQziBwBfvLiHjaPGxCKLoJi 63 | 1NpYjtLira16LfGbGwZJ5JbDPh3ai9bjf4 64 | 16jY7qLJnxb7CHZyqBP8qca9d51gAjyXQN 65 | 18ZMbwUFLMHoZBbfpCjUJQTCMCbktshgpe 66 | 13zb1hQbWVsc2S7ZTZnP2G4undNNpdh5so 67 | 1BY8GQbnueYofwSuFAT3USAhGjPrkxDdW9 68 | 1MVDYgVaSN6iKKEsbzRUAYFrYJadLYZvvZ 69 | 19vkiEajfhuZ8bs8Zu2jgmC6oqZbWqhxhG 70 | 19YZECXj3SxEZMoUeJ1yiPsw8xANe7M7QR 71 | 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU 72 | 1JTK7s9YVYywfm5XUH7RNhHJH1LshCaRFR 73 | 12VVRNPi4SJqUTsp6FmqDqY5sGosDtysn4 74 | 1FWGcVDK3JGzCC3WtkYetULPszMaK2Jksv 75 | 1J36UjUByGroXcCvmj13U6uwaVv9caEeAt 76 | 1DJh2eHFYQfACPmrvpyWc8MSTYKh7w9eRF 77 | 1Bxk4CQdqL9p22JEtDfdXMsng1XacifUtE 78 | 15qF6X51huDjqTmF9BJgxXdt1xcj46Jmhb 79 | 1ARk8HWJMn8js8tQmGUJeQHjSE7KRkn2t8 80 | 1BCf6rHUW6m3iH2ptsvnjgLruAiPQQepLe 81 | 15qsCm78whspNQFydGJQk5rexzxTQopnHZ 82 | 13zYrYhhJxp6Ui1VV7pqa5WDhNWM45ARAC 83 | 14MdEb4eFcT3MVG5sPFG4jGLuHJSnt1Dk2 84 | 1CMq3SvFcVEcpLMuuH8PUcNiqsK1oicG2D 85 | 1Kh22PvXERd2xpTQk3ur6pPEqFeckCJfAr 86 | 1K3x5L6G57Y494fDqBfrojD28UJv4s5JcK 87 | 1PxH3K1Shdjb7gSEoTX7UPDZ6SH4qGPrvq 88 | 16AbnZjZZipwHMkYKBSfswGWKDmXHjEpSf 89 | 19QciEHbGVNY4hrhfKXmcBBCrJSBZ6TaVt 90 | 1L12FHH2FHjvTviyanuiFVfmzCy46RRATU 91 | 1EzVHtmbN4fs4MiNk3ppEnKKhsmXYJ4s74 92 | 1AE8NzzgKE7Yhz7BWtAcAAxiFMbPo82NB5 93 | 17Q7tuG2JwFFU9rXVj3uZqRtioH3mx2Jad 94 | 1K6xGMUbs6ZTXBnhw1pippqwK6wjBWtNpL 95 | 19eVSDuizydXxhohGh8Ki9WY9KsHdSwoQC 96 | 15ANYzzCp5BFHcCnVFzXqyibpzgPLWaD8b 97 | 18ywPwj39nGjqBrQJSzZVq2izR12MDpDr8 98 | 1CaBVPrwUxbQYYswu32w7Mj4HR4maNoJSX 99 | 1JWnE6p6UN7ZJBN7TtcbNDoRcjFtuDWoNL 100 | 1KCgMv8fo2TPBpddVi9jqmMmcne9uSNJ5F 101 | 1CKCVdbDJasYmhswB6HKZHEAnNaDpK7W4n 102 | 1PXv28YxmYMaB8zxrKeZBW8dt2HK7RkRPX 103 | 1AcAmB6jmtU6AiEcXkmiNE9TNVPsj9DULf 104 | 1EQJvpsmhazYCcKX5Au6AZmZKRnzarMVZu 105 | 1CMjscKB3QW7SDyQ4c3C3DEUHiHRhiZVib 106 | 18KsfuHuzQaBTNLASyj15hy4LuqPUo1FNB 107 | 15EJFC5ZTs9nhsdvSUeBXjLAuYq3SWaxTc 108 | 1HB1iKUqeffnVsvQsbpC6dNi1XKbyNuqao 109 | 1GvgAXVCbA8FBjXfWiAms4ytFeJcKsoyhL 110 | 12JzYkkN76xkwvcPT6AWKZtGX6w2LAgsJg 111 | 1824ZJQ7nKJ9QFTRBqn7z7dHV5EGpzUpH3 112 | 18A7NA9FTsnJxWgkoFfPAFbQzuQxpRtCos 113 | 1NeGn21dUDDeqFQ63xb2SpgUuXuBLA4WT4 114 | 174SNxfqpdMGYy5YQcfLbSTK3MRNZEePoy 115 | 1NLbHuJebVwUZ1XqDjsAyfTRUPwDQbemfv 116 | 1MnJ6hdhvK37VLmqcdEwqC3iFxyWH2PHUV 117 | 1KNRfGWw7Q9Rmwsc6NT5zsdvEb9M2Wkj5Z 118 | 1PJZPzvGX19a7twf5HyD2VvNiPdHLzm9F6 119 | 1GuBBhf61rnvRe4K8zu8vdQB3kHzwFqSy7 120 | 17s2b9ksz5y7abUm92cHwG8jEPCzK3dLnT 121 | 1GDSuiThEV64c166LUFC9uDcVdGjqkxKyh 122 | 1Me3ASYt5JCTAK2XaC32RMeH34PdprrfDx 123 | 1CdufMQL892A69KXgv6UNBD17ywWqYpKut 124 | 1BkkGsX9ZM6iwL3zbqs7HWBV7SvosR6m8N 125 | 1PXAyUB8ZoH3WD8n5zoAthYjN15yN5CVq5 126 | 1AWCLZAjKbV1P7AHvaPNCKiB7ZWVDMxFiz 127 | 1G6EFyBRU86sThN3SSt3GrHu1sA7w7nzi4 128 | 1MZ2L1gFrCtkkn6DnTT2e4PFUTHw9gNwaj 129 | 1Hz3uv3nNZzBVMXLGadCucgjiCs5W9vaGz 130 | 1Fo65aKq8s8iquMt6weF1rku1moWVEd5Ua 131 | 16zRPnT8znwq42q7XeMkZUhb1bKqgRogyy 132 | 1KrU4dHE5WrW8rhWDsTRjR21r8t3dsrS3R 133 | 17uDfp5r4n441xkgLFmhNoSW1KWp6xVLD 134 | 13A3JrvXmvg5w9XGvyyR4JEJqiLz8ZySY3 135 | 16RGFo6hjq9ym6Pj7N5H7L1NR1rVPJyw2v 136 | 1UDHPdovvR985NrWSkdWQDEQ1xuRiTALq 137 | 15nf31J46iLuK1ZkTnqHo7WgN5cARFK3RA 138 | 1Ab4vzG6wEQBDNQM1B2bvUz4fqXXdFk2WT 139 | 1Fz63c775VV9fNyj25d9Xfw3YHE6sKCxbt 140 | 1QKBaU6WAeycb3DbKbLBkX7vJiaS8r42Xo 141 | 1CD91Vm97mLQvXhrnoMChhJx4TP9MaQkJo 142 | 15MnK2jXPqTMURX4xC3h4mAZxyCcaWWEDD 143 | 13N66gCzWWHEZBxhVxG18P8wyjEWF9Yoi1 144 | 1NevxKDYuDcCh1ZMMi6ftmWwGrZKC6j7Ux 145 | 19GpszRNUej5yYqxXoLnbZWKew3KdVLkXg 146 | 1M7ipcdYHey2Y5RZM34MBbpugghmjaV89P 147 | 18aNhurEAJsw6BAgtANpexk5ob1aGTwSeL 148 | 1FwZXt6EpRT7Fkndzv6K4b4DFoT4trbMrV 149 | 1CXvTzR6qv8wJ7eprzUKeWxyGcHwDYP1i2 150 | 1MUJSJYtGPVGkBCTqGspnxyHahpt5Te8jy 151 | 13Q84TNNvgcL3HJiqQPvyBb9m4hxjS3jkV 152 | 1LuUHyrQr8PKSvbcY1v1PiuGuqFjWpDumN 153 | 18192XpzzdDi2K11QVHR7td2HcPS6Qs5vg 154 | 1NgVmsCCJaKLzGyKLFJfVequnFW9ZvnMLN 155 | 1AoeP37TmHdFh8uN72fu9AqgtLrUwcv2wJ 156 | 1FTpAbQa4h8trvhQXjXnmNhqdiGBd1oraE 157 | 14JHoRAdmJg3XR4RjMDh6Wed6ft6hzbQe9 158 | 19z6waranEf8CcP8FqNgdwUe1QRxvUNKBG 159 | 14u4nA5sugaswb6SZgn5av2vuChdMnD9E5 160 | 1NBC8uXJy1GiJ6drkiZa1WuKn51ps7EPTv 161 | -------------------------------------------------------------------------------- /libs/Range_gui.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Team Mizogg 3 | """ 4 | from PyQt6.QtCore import QMetaObject, Qt, Q_ARG 5 | from PyQt6.QtWidgets import * 6 | from libs import console_gui 7 | 8 | class RangeDialog(QDialog): 9 | def __init__(self, parent=None): 10 | super().__init__(parent) 11 | 12 | self.setWindowTitle("Hexadecimal Range Calculator") 13 | self.resize(800, 600) 14 | 15 | mainLayout = QVBoxLayout(self) 16 | 17 | keyspaceGroupBox = self.create_keyspaceGroupBox() 18 | mainLayout.addWidget(keyspaceGroupBox) 19 | 20 | self.consoleWindow = console_gui.ConsoleWindow(self) 21 | mainLayout.addWidget(self.consoleWindow) 22 | 23 | 24 | def create_keyspaceGroupBox(self): 25 | keyspaceGroupBox = QGroupBox(self) 26 | keyspaceGroupBox.setTitle("Key Space Configuration") 27 | keyspaceGroupBox.setStyleSheet("QGroupBox { border: 3px solid; padding: 5px; }") 28 | keyspaceMainLayout = QVBoxLayout(keyspaceGroupBox) 29 | 30 | self.keyspaceLabel = QLabel("Key Space:", self) 31 | keyspaceMainLayout.addWidget(self.keyspaceLabel) 32 | 33 | self.start_edit = QLineEdit(self) 34 | self.start_edit.setPlaceholderText('Start Range') 35 | self.start_edit.setToolTip(' Type the start range in HEX ') 36 | keyspaceMainLayout.addWidget(self.start_edit) 37 | 38 | self.end_edit = QLineEdit(self) 39 | self.end_edit.setPlaceholderText('End Range') 40 | self.end_edit.setToolTip(' Type the end range in HEX ') 41 | keyspaceMainLayout.addWidget(self.end_edit) 42 | 43 | self.keyspace_slider = QSlider(Qt.Orientation.Horizontal) 44 | self.keyspace_slider.setMinimum(1) 45 | self.keyspace_slider.setMaximum(256) 46 | self.keyspace_slider.setValue(71) 47 | self.keyspace_slider.setToolTip(' Drag Left to Right to Adjust Range ') 48 | keyspacerange_layout = QVBoxLayout() 49 | keyspacerange_layout.addWidget(self.start_edit) 50 | keyspacerange_layout.addWidget(self.end_edit) 51 | keyspacerange_layout.addWidget(self.keyspace_slider) 52 | 53 | keyspaceMainLayout.addLayout(keyspacerange_layout) 54 | 55 | self.keyspace_slider.valueChanged.connect(self.update_keyspace_range) 56 | self.bitsLabel = QLabel("Bits:", self) 57 | keyspaceMainLayout.addWidget(self.bitsLabel) 58 | 59 | self.bitsLineEdit = QLineEdit(self) 60 | self.bitsLineEdit.setText("71") 61 | self.bitsLineEdit.textChanged.connect(self.updateSliderAndRanges) 62 | keyspaceMainLayout.addWidget(self.bitsLineEdit) 63 | 64 | percentLayout = QHBoxLayout() 65 | percentLabel = QLabel("Percentage:") 66 | percentLayout.addWidget(percentLabel) 67 | self.percentLineEdit = QLineEdit("90") 68 | percentLayout.addWidget(self.percentLineEdit) 69 | keyspaceMainLayout.addLayout(percentLayout) 70 | 71 | calculateButton = QPushButton("Calculate", self) 72 | calculateButton.setStyleSheet( 73 | "QPushButton { font-size: 10pt; }" 74 | "QPushButton:hover { font-size: 10pt; }" 75 | ) 76 | calculateButton.clicked.connect(self.calculate_percentage) 77 | keyspaceMainLayout.addWidget(calculateButton) 78 | 79 | hexRangeGroupBox = self.create_hexRangeGroupBox() 80 | keyspaceMainLayout.addWidget(hexRangeGroupBox) 81 | 82 | return keyspaceGroupBox 83 | 84 | def create_hexRangeGroupBox(self): 85 | hexRangeGroupBox = QGroupBox(self) 86 | hexRangeGroupBox.setTitle("Range Division Tools") 87 | hexRangeGroupBox.setStyleSheet("QGroupBox { border: 3px solid; padding: 5px; }") 88 | hexRangeMainLayout = QVBoxLayout(hexRangeGroupBox) 89 | 90 | power_label = QLabel("Show") 91 | power_label.setObjectName("powerLabel") 92 | self.format_combo_box_divs = QComboBox() 93 | self.format_combo_box_divs.addItems( 94 | ['1', '2', '4', '8', '16', '32', '64', '128', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536'] 95 | ) 96 | select_power_layout = QHBoxLayout() 97 | select_power_layout.setAlignment( 98 | Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft 99 | ) 100 | select_power_layout.addWidget(power_label) 101 | select_power_layout.addWidget(self.format_combo_box_divs) 102 | 103 | self.Check_button = QPushButton("Check") 104 | self.Check_button.setStyleSheet( 105 | "QPushButton { font-size: 10pt; }" 106 | "QPushButton:hover { font-size: 10pt; }" 107 | ) 108 | self.Check_button.clicked.connect(self.div_range) 109 | hexRangeMainLayout.addLayout(select_power_layout) 110 | hexRangeMainLayout.addWidget(self.Check_button) 111 | 112 | calculateButton1 = QPushButton("Calculate and Percent", self) 113 | calculateButton1.setStyleSheet( 114 | "QPushButton { font-size: 10pt; }" 115 | "QPushButton:hover { font-size: 10pt; }" 116 | ) 117 | calculateButton1.clicked.connect(self.calculate_percentage_and_div_range) 118 | hexRangeMainLayout.addWidget(calculateButton1) 119 | 120 | return hexRangeGroupBox 121 | 122 | def div_range(self): 123 | try: 124 | start_value = self.start_edit.text() 125 | end_value = self.end_edit.text() 126 | num_divs = int(self.format_combo_box_divs.currentText()) 127 | self.start_hex = int(start_value, 16) 128 | self.end_hex = int(end_value, 16) 129 | 130 | chunk_size = (self.end_hex - self.start_hex) // num_divs 131 | 132 | if self.end_hex < self.start_hex: 133 | error_message = "Start HEX is greater than Stop HEX" 134 | self.consoleWindow.append_output(error_message) 135 | else: 136 | ranges = [(self.start_hex + i * chunk_size, self.start_hex + (i + 1) * chunk_size) for i in range(num_divs)] 137 | start_index = self.start_hex 138 | 139 | for i, (priv_start, priv_end) in enumerate(ranges, start=1): 140 | priv_start_hex = f"{priv_start:X}" 141 | priv_end_hex = f"{priv_end:X}" 142 | 143 | if start_index >= priv_start and start_index < priv_end: 144 | displayprint = f' Range {i}:\t{priv_start_hex} - {priv_end_hex}\t<<-- Current Range' 145 | else: 146 | displayprint = f' Range {i}:\t{priv_start_hex} - {priv_end_hex}' 147 | 148 | self.consoleWindow.append_output(displayprint) 149 | 150 | except ValueError as e: 151 | error_message = f"Value Error: {str(e)}" 152 | self.consoleWindow.append_output(error_message) 153 | 154 | 155 | def update_keyspace_range(self, value): 156 | start_range = 2 ** (value - 1) 157 | end_range = 2 ** value - 1 158 | self.start_edit.setText(f"{start_range:X}") 159 | self.end_edit.setText(f"{end_range:X}") 160 | self.bitsLineEdit.setText(str(value)) 161 | 162 | def updateSliderAndRanges(self, text): 163 | try: 164 | bits = int(text) 165 | self.keyspace_slider.setValue(bits) 166 | 167 | if bits == 256: 168 | start_range = "8000000000000000000000000000000000000000000000000000000000000000" 169 | end_range = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140" 170 | else: 171 | start_range = 2 ** (bits - 1) 172 | end_range = 2 ** bits - 1 173 | start_range = f"{start_range:X}" 174 | end_range = f"{end_range:X}" 175 | except ValueError: 176 | range_message = "Range should be in Bit 1-256 " 177 | QMessageBox.information(self, "Range Error", range_message) 178 | 179 | def calculate_percentage(self): 180 | try: 181 | start_hex = self.start_edit.text() 182 | end_hex = self.end_edit.text() 183 | start = int(start_hex, 16) 184 | end = int(end_hex, 16) 185 | percent = float(self.percentLineEdit.text()) / 100 186 | 187 | total_keys = end - start # Calculate total keys in the current range 188 | 189 | result = start + int(percent * total_keys) 190 | result_hex = hex(result).upper().replace("0X", "") 191 | 192 | # Calculate percentage based on the current range 193 | percentage = (result - start) / total_keys * 100 194 | 195 | output = ( 196 | f"Range: {start_hex}:{end_hex}\n" 197 | f"Result: {result_hex}\n" 198 | f"Keys in this Range: {int(percent * total_keys):,}\n" 199 | f"Calculated Percentage: {percentage:.5f}% of Keys in this Range" 200 | ) 201 | 202 | self.consoleWindow.append_output(output) 203 | 204 | # Update start for the next calculation 205 | start = result 206 | 207 | except ValueError as e: 208 | QMessageBox.information(self, "Input Error", str(e)) 209 | 210 | def calculate_percentage_and_div_range(self): 211 | try: 212 | start_hex = self.start_edit.text() 213 | end_hex = self.end_edit.text() 214 | self.start_hex = int(start_hex, 16) 215 | self.end_hex = int(end_hex, 16) 216 | 217 | num_divs = int(self.format_combo_box_divs.currentText()) 218 | chunk_size = (self.end_hex - self.start_hex) // num_divs 219 | 220 | if self.end_hex < self.start_hex: 221 | error_range = ( 222 | f'\n\n !!!!! ERROR !!!!!! \n Your Start HEX {start_hex} is MORE than your Stop HEX {end_hex}' 223 | ) 224 | self.consoleWindow.append_output(error_range) 225 | else: 226 | ranges = [(self.start_hex + i * chunk_size, self.start_hex + (i + 1) * chunk_size) for i in range(num_divs)] 227 | start_index = self.start_hex 228 | 229 | for i, (priv_start, priv_end) in enumerate(ranges, start=1): 230 | # Format ranges without '0x' and in uppercase 231 | priv_start_hex = f"{priv_start:X}" 232 | priv_end_hex = f"{priv_end:X}" 233 | 234 | percent = float(self.percentLineEdit.text()) / 100 235 | start = int(priv_start_hex, 16) 236 | end = int(priv_end_hex, 16) 237 | 238 | total_keys = end - start # Calculate total keys for the current range 239 | result = start + int(percent * total_keys) 240 | result_hex = hex(result).upper().replace("0X", "") 241 | 242 | percentage = (result - start) / total_keys * 100 243 | 244 | percentage_output = ( 245 | f"Range {i}:\t{priv_start_hex} - {priv_end_hex}\n" 246 | f"Result: {result_hex}\n" 247 | f"Keys in this Range: {int(percent * total_keys):,}\n" 248 | f"Calculated Percentage: {percentage:.5f}%\n" 249 | ) 250 | 251 | if start_index >= priv_start and start_index < priv_end: 252 | percentage_output += "<<-- Current Range" 253 | 254 | self.consoleWindow.append_output(percentage_output) 255 | 256 | except ValueError as e: 257 | QMessageBox.information(self, "Input Error", str(e)) -------------------------------------------------------------------------------- /libs/theme_manager.py: -------------------------------------------------------------------------------- 1 | from PyQt6.QtGui import QColor, QPalette, QPixmap 2 | from PyQt6.QtWidgets import (QApplication, QDialog, QVBoxLayout, QHBoxLayout, 3 | QLabel, QComboBox, QPushButton, QWidget, QCheckBox, 4 | QLineEdit, QGroupBox, QFormLayout, QTabWidget) 5 | from PyQt6.QtCore import Qt, pyqtSignal 6 | import configparser 7 | 8 | # Initialize config parser 9 | config = configparser.ConfigParser() 10 | config.read('config.ini') 11 | 12 | class SettingsDialog(QDialog): 13 | """ 14 | Dialog for configuring application settings 15 | """ 16 | settings_changed = pyqtSignal() 17 | 18 | def __init__(self, parent=None): 19 | super().__init__(parent) 20 | self.setWindowTitle("AI Bitcoin Private Key Search Settings") 21 | self.setMinimumWidth(600) 22 | self.setMinimumHeight(400) 23 | self.init_ui() 24 | 25 | def init_ui(self): 26 | """Initialize the UI components""" 27 | main_layout = QVBoxLayout() 28 | 29 | self.tab_widget = QTabWidget() 30 | 31 | self.appearance_tab = QWidget() 32 | 33 | self.tab_widget.addTab(self.appearance_tab, "Appearance") 34 | 35 | 36 | self.setup_appearance_tab() 37 | 38 | 39 | # Add tab widget to main layout 40 | main_layout.addWidget(self.tab_widget) 41 | 42 | # Add buttons 43 | button_layout = QHBoxLayout() 44 | 45 | self.save_button = QPushButton("Save") 46 | self.save_button.clicked.connect(self.save_settings) 47 | 48 | self.cancel_button = QPushButton("Cancel") 49 | self.cancel_button.clicked.connect(self.reject) 50 | 51 | self.apply_button = QPushButton("Apply") 52 | self.apply_button.clicked.connect(self.apply_settings) 53 | 54 | button_layout.addStretch() 55 | button_layout.addWidget(self.save_button) 56 | button_layout.addWidget(self.apply_button) 57 | button_layout.addWidget(self.cancel_button) 58 | 59 | main_layout.addLayout(button_layout) 60 | 61 | self.setLayout(main_layout) 62 | 63 | 64 | def setup_appearance_tab(self): 65 | """Setup the appearance tab""" 66 | layout = QVBoxLayout() 67 | 68 | # Theme settings group 69 | theme_group = QGroupBox("Theme Settings") 70 | theme_layout = QFormLayout() 71 | 72 | self.theme_combo = QComboBox() 73 | themes = list(theme_manager.themes.keys()) 74 | self.theme_combo.addItems(themes) 75 | self.theme_combo.setCurrentText(theme_manager.current_theme) 76 | 77 | # Add theme preview 78 | self.theme_preview = QLabel() 79 | self.theme_preview.setFixedSize(300, 200) 80 | self.update_theme_preview(theme_manager.current_theme) 81 | 82 | self.theme_combo.currentTextChanged.connect(self.update_theme_preview) 83 | 84 | theme_layout.addRow("Theme:", self.theme_combo) 85 | theme_layout.addRow("Preview:", self.theme_preview) 86 | 87 | theme_group.setLayout(theme_layout) 88 | 89 | # Add groups to layout 90 | layout.addWidget(theme_group) 91 | layout.addStretch() 92 | 93 | self.appearance_tab.setLayout(layout) 94 | 95 | def update_theme_preview(self, theme_name): 96 | """Update the theme preview to show a realistic representation of the theme""" 97 | theme = theme_manager.get_theme(theme_name) 98 | 99 | # Create a preview widget to demonstrate theme elements 100 | preview_widget = QWidget() 101 | preview_widget.setFixedSize(300, 300) 102 | 103 | # Create a layout for the preview 104 | layout = QVBoxLayout(preview_widget) 105 | layout.setContentsMargins(10, 10, 10, 10) 106 | layout.setSpacing(8) 107 | 108 | # Add a title bar to simulate window 109 | title_bar = QWidget() 110 | title_bar.setFixedHeight(30) 111 | title_bar_layout = QHBoxLayout(title_bar) 112 | title_bar_layout.setContentsMargins(8, 0, 8, 0) 113 | 114 | title_label = QLabel("Preview Window") 115 | close_button = QPushButton("×") 116 | close_button.setFixedSize(20, 20) 117 | 118 | title_bar_layout.addWidget(title_label) 119 | title_bar_layout.addStretch() 120 | title_bar_layout.addWidget(close_button) 121 | 122 | # Create content area 123 | content_area = QWidget() 124 | content_layout = QVBoxLayout(content_area) 125 | 126 | # Add some common UI elements 127 | button = QPushButton("Sample Button") 128 | checkbox = QCheckBox("Enable Option") 129 | combo = QComboBox() 130 | combo.addItems(["Option 1", "Option 2", "Option 3"]) 131 | text_input = QLineEdit() 132 | text_input.setPlaceholderText("Enter text...") 133 | 134 | content_layout.addWidget(button) 135 | content_layout.addWidget(checkbox) 136 | content_layout.addWidget(combo) 137 | content_layout.addWidget(text_input) 138 | content_layout.addStretch() 139 | 140 | # Add status bar 141 | status_bar = QLabel("Status: Ready") 142 | status_bar.setFixedHeight(25) 143 | status_bar.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter) 144 | 145 | # Add all components to main layout 146 | layout.addWidget(title_bar) 147 | layout.addWidget(content_area) 148 | layout.addWidget(status_bar) 149 | 150 | # Apply theme colors 151 | preview_style = f""" 152 | QWidget {{ 153 | background-color: {theme['background_color']}; 154 | color: {theme['text_color']}; 155 | border: none; 156 | }} 157 | 158 | QLabel {{ 159 | padding: 2px; 160 | }} 161 | 162 | QPushButton {{ 163 | background-color: {theme['button_color']}; 164 | color: {theme['button_text_color']}; 165 | border: 1px solid {theme['border_color']}; 166 | border-radius: 4px; 167 | padding: 5px; 168 | min-width: 80px; 169 | }} 170 | 171 | QPushButton:hover {{ 172 | background-color: {theme['button_hover_color']}; 173 | }} 174 | 175 | QLineEdit {{ 176 | background-color: {theme['background_color']}; 177 | color: {theme['text_color']}; 178 | border: 1px solid {theme['border_color']}; 179 | border-radius: 4px; 180 | padding: 5px; 181 | }} 182 | 183 | QComboBox {{ 184 | background-color: {theme['background_color']}; 185 | color: {theme['text_color']}; 186 | border: 1px solid {theme['border_color']}; 187 | border-radius: 4px; 188 | padding: 5px; 189 | min-width: 100px; 190 | }} 191 | 192 | QComboBox::drop-down {{ 193 | border: none; 194 | width: 20px; 195 | }} 196 | 197 | QCheckBox {{ 198 | spacing: 5px; 199 | }} 200 | 201 | QCheckBox::indicator {{ 202 | width: 16px; 203 | height: 16px; 204 | border: 1px solid {theme['border_color']}; 205 | border-radius: 3px; 206 | background-color: {theme['background_color']}; 207 | }} 208 | 209 | QCheckBox::indicator:checked {{ 210 | background-color: {theme['accent_color']}; 211 | }} 212 | """ 213 | 214 | preview_widget.setStyleSheet(preview_style) 215 | 216 | pixmap = QPixmap(preview_widget.size()) 217 | preview_widget.render(pixmap) 218 | 219 | self.theme_preview.setPixmap(pixmap) 220 | 221 | def save_settings(self): 222 | """Save settings and close dialog""" 223 | self.apply_settings() 224 | self.accept() 225 | 226 | def apply_settings(self): 227 | """Apply settings without closing dialog""" 228 | # Save theme settings 229 | theme_name = self.theme_combo.currentText() 230 | if theme_name != theme_manager.current_theme: 231 | theme_manager.set_theme(theme_name) 232 | if not config.has_section('theme'): 233 | config.add_section('theme') 234 | config.set('theme', 'name', theme_name) 235 | 236 | # Save to file 237 | with open('config.ini', 'w') as configfile: 238 | config.write(configfile) 239 | 240 | # Apply theme to all windows 241 | stylesheet = theme_manager.get_stylesheet() 242 | QApplication.instance().setStyleSheet(stylesheet) 243 | 244 | # Emit signal to notify of changes 245 | self.settings_changed.emit() 246 | 247 | class ThemeManager: 248 | """ 249 | Manages application themes and provides styling 250 | """ 251 | def __init__(self): 252 | self.themes = { 253 | "default": { 254 | "description": "Defualt Black A sleek and minimalistic black theme with bold red accents", 255 | "primary_color": "#E7481F", 256 | "secondary_color": "#A13316", 257 | "background_color": "#000000", 258 | "text_color": "#FFFFFF", 259 | "accent_color": "#FFD700", 260 | "button_color": "#E7481F", 261 | "button_text_color": "#FFFFFF", 262 | "button_hover_color": "#A13316", 263 | "tab_color": "#3D3D3D", 264 | "tab_selected_color": "#E7481F", 265 | "tab_text_color": "#FFFFFF", 266 | "tab_selected_text_color": "#FFFFFF", 267 | "border_color": "#555555", 268 | "success_color": "#28A745", 269 | "warning_color": "#FFC107", 270 | "error_color": "#DC3545", 271 | "info_color": "#17A2B8" 272 | }, 273 | "bitcoin": { 274 | "description": "A bright and bold theme inspired by Bitcoin's signature orange and gold.", 275 | "primary_color": "#F7931A", 276 | "secondary_color": "#D68411", 277 | "background_color": "#FFFFFF", 278 | "text_color": "#4D4D4D", 279 | "accent_color": "#FFD700", 280 | "button_color": "#F7931A", 281 | "button_text_color": "#FFFFFF", 282 | "button_hover_color": "#D68411", 283 | "tab_color": "#F5F5F5", 284 | "tab_selected_color": "#F7931A", 285 | "tab_text_color": "#4D4D4D", 286 | "tab_selected_text_color": "#FFFFFF", 287 | "border_color": "#CCCCCC", 288 | "success_color": "#28A745", 289 | "warning_color": "#FFC107", 290 | "error_color": "#DC3545", 291 | "info_color": "#17A2B8" 292 | }, 293 | 294 | "black": { 295 | "description": "A sleek and minimalistic black theme with bold red accents", 296 | "primary_color": "#E7481F", 297 | "secondary_color": "#A13316", 298 | "background_color": "#000000", 299 | "text_color": "#FFFFFF", 300 | "accent_color": "#FFD700", 301 | "button_color": "#E7481F", 302 | "button_text_color": "#FFFFFF", 303 | "button_hover_color": "#A13316", 304 | "tab_color": "#3D3D3D", 305 | "tab_selected_color": "#E7481F", 306 | "tab_text_color": "#FFFFFF", 307 | "tab_selected_text_color": "#FFFFFF", 308 | "border_color": "#555555", 309 | "success_color": "#28A745", 310 | "warning_color": "#FFC107", 311 | "error_color": "#DC3545", 312 | "info_color": "#17A2B8" 313 | }, 314 | 315 | "cyberpunk": { 316 | "description": "A futuristic neon theme inspired by cyberpunk aesthetics and city lights.", 317 | "primary_color": "#0FF0FC", 318 | "secondary_color": "#FF007F", 319 | "background_color": "#1A1A2E", 320 | "text_color": "#0FF0FC", 321 | "accent_color": "#FFD700", 322 | "button_color": "#FF007F", 323 | "button_text_color": "#FFFFFF", 324 | "button_hover_color": "#D4006A", 325 | "tab_color": "#16213E", 326 | "tab_selected_color": "#FF007F", 327 | "tab_text_color": "#0FF0FC", 328 | "tab_selected_text_color": "#FFFFFF", 329 | "border_color": "#FF007F", 330 | "success_color": "#00FF00", 331 | "warning_color": "#FFC107", 332 | "error_color": "#FF3131", 333 | "info_color": "#0FF0FC" 334 | }, 335 | 336 | "dark": { 337 | "description": "A modern dark theme with deep gray tones and striking red highlights.", 338 | "primary_color": "#E7481F", 339 | "secondary_color": "#A13316", 340 | "background_color": "#2D2D2D", 341 | "text_color": "#FFFFFF", 342 | "accent_color": "#FFD700", 343 | "button_color": "#E7481F", 344 | "button_text_color": "#FFFFFF", 345 | "button_hover_color": "#A13316", 346 | "tab_color": "#3D3D3D", 347 | "tab_selected_color": "#E7481F", 348 | "tab_text_color": "#FFFFFF", 349 | "tab_selected_text_color": "#FFFFFF", 350 | "border_color": "#555555", 351 | "success_color": "#28A745", 352 | "warning_color": "#FFC107", 353 | "error_color": "#DC3545", 354 | "info_color": "#17A2B8" 355 | }, 356 | 357 | "devil_flaming": { 358 | "description": "Fiery reds, dark shadows, and an intense, infernal vibe 🔥😈", 359 | "primary_color": "#FF0000", 360 | "secondary_color": "#8B0000", 361 | "background_color": "#1E0000", 362 | "text_color": "#FF4500", 363 | "accent_color": "#FFD700", 364 | "button_color": "#B22222", 365 | "button_text_color": "#FFFFFF", 366 | "button_hover_color": "#FF4500", 367 | "tab_color": "#300000", 368 | "tab_selected_color": "#FF0000", 369 | "tab_text_color": "#FF6347", 370 | "tab_selected_text_color": "#FFFFFF", 371 | "border_color": "#8B0000", 372 | "success_color": "#FF4500", 373 | "warning_color": "#FFA500", 374 | "error_color": "#8B0000", 375 | "info_color": "#FF6347" 376 | }, 377 | 378 | "ice_blue": { 379 | "description": "Frozen whites, icy blues, and chilling cold vibes ❄️💙", 380 | "primary_color": "#00BFFF", 381 | "secondary_color": "#1E90FF", 382 | "background_color": "#E0F7FA", 383 | "text_color": "#005F8F", 384 | "accent_color": "#FFFFFF", 385 | "button_color": "#00CED1", 386 | "button_text_color": "#FFFFFF", 387 | "button_hover_color": "#4682B4", 388 | "tab_color": "#B0E0E6", 389 | "tab_selected_color": "#00BFFF", 390 | "tab_text_color": "#005F8F", 391 | "tab_selected_text_color": "#FFFFFF", 392 | "border_color": "#A0C4FF", 393 | "success_color": "#00FA9A", 394 | "warning_color": "#87CEFA", 395 | "error_color": "#4682B4", 396 | "info_color": "#00FFFF" 397 | }, 398 | 399 | "light": { 400 | "description": "A bright and clean theme with soft gray and warm red elements.", 401 | "primary_color": "#E7481F", 402 | "secondary_color": "#A13316", 403 | "background_color": "#F8F9FA", 404 | "text_color": "#212529", 405 | "accent_color": "#FFD700", 406 | "button_color": "#E7481F", 407 | "button_text_color": "#FFFFFF", 408 | "button_hover_color": "#A13316", 409 | "tab_color": "#E9ECEF", 410 | "tab_selected_color": "#E7481F", 411 | "tab_text_color": "#212529", 412 | "tab_selected_text_color": "#FFFFFF", 413 | "border_color": "#DEE2E6", 414 | "success_color": "#28A745", 415 | "warning_color": "#FFC107", 416 | "error_color": "#DC3545", 417 | "info_color": "#17A2B8" 418 | }, 419 | 420 | "matrix": { 421 | "description": "A hacker-inspired green-on-black theme straight out of The Matrix.", 422 | "primary_color": "#00FF00", 423 | "secondary_color": "#008800", 424 | "background_color": "#000000", 425 | "text_color": "#00FF00", 426 | "accent_color": "#FFFFFF", 427 | "button_color": "#008800", 428 | "button_text_color": "#00FF00", 429 | "button_hover_color": "#00AA00", 430 | "tab_color": "#001100", 431 | "tab_selected_color": "#00FF00", 432 | "tab_text_color": "#00FF00", 433 | "tab_selected_text_color": "#000000", 434 | "border_color": "#00FF00", 435 | "success_color": "#00FF00", 436 | "warning_color": "#FFFF00", 437 | "error_color": "#FF0000", 438 | "info_color": "#00FFFF" 439 | }, 440 | 441 | "unicorn": { 442 | "description": "A playful and magical pastel theme with pinks, purples, and dreamy colors.", 443 | "primary_color": "#FF69B4", 444 | "secondary_color": "#9370DB", 445 | "background_color": "#F0F8FF", 446 | "text_color": "#4B0082", 447 | "accent_color": "#FFD700", 448 | "button_color": "#FF69B4", 449 | "button_text_color": "#FFFFFF", 450 | "button_hover_color": "#9370DB", 451 | "tab_color": "#E6E6FA", 452 | "tab_selected_color": "#FF69B4", 453 | "tab_text_color": "#4B0082", 454 | "tab_selected_text_color": "#FFFFFF", 455 | "border_color": "#D8BFD8", 456 | "success_color": "#00FF00", 457 | "warning_color": "#FFFF00", 458 | "error_color": "#FF0000", 459 | "info_color": "#00FFFF" 460 | } 461 | } 462 | self.current_theme = "black" 463 | 464 | def get_theme(self, theme_name=None): 465 | """Get a theme by name or the current theme if none specified""" 466 | if theme_name is None: 467 | theme_name = self.current_theme 468 | 469 | return self.themes.get(theme_name, self.themes["default"]) 470 | 471 | def set_theme(self, theme_name): 472 | """Set the current theme""" 473 | if theme_name in self.themes: 474 | self.current_theme = theme_name 475 | return True 476 | return False 477 | 478 | def apply_theme(self, app): 479 | """Apply the current theme to the application""" 480 | if not isinstance(app, QApplication): 481 | raise TypeError("Expected QApplication instance") 482 | 483 | theme = self.get_theme() 484 | 485 | # Create a palette for the application 486 | palette = QPalette() 487 | 488 | # Set colors based on theme 489 | palette.setColor(QPalette.ColorRole.Window, QColor(theme["background_color"])) 490 | palette.setColor(QPalette.ColorRole.WindowText, QColor(theme["text_color"])) 491 | palette.setColor(QPalette.ColorRole.Base, QColor(theme["background_color"])) 492 | palette.setColor(QPalette.ColorRole.AlternateBase, QColor(theme["tab_color"])) 493 | palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(theme["background_color"])) 494 | palette.setColor(QPalette.ColorRole.ToolTipText, QColor(theme["text_color"])) 495 | palette.setColor(QPalette.ColorRole.Text, QColor(theme["text_color"])) 496 | palette.setColor(QPalette.ColorRole.Button, QColor(theme["button_color"])) 497 | palette.setColor(QPalette.ColorRole.ButtonText, QColor(theme["button_text_color"])) 498 | palette.setColor(QPalette.ColorRole.BrightText, QColor(theme["accent_color"])) 499 | palette.setColor(QPalette.ColorRole.Link, QColor(theme["primary_color"])) 500 | palette.setColor(QPalette.ColorRole.Highlight, QColor(theme["primary_color"])) 501 | palette.setColor(QPalette.ColorRole.HighlightedText, QColor(theme["button_text_color"])) 502 | 503 | # Apply the palette to the application 504 | app.setPalette(palette) 505 | 506 | return self.get_stylesheet() 507 | 508 | 509 | def get_stylesheet(self): 510 | """Get the stylesheet for the current theme""" 511 | theme = self.get_theme() 512 | 513 | return f""" 514 | QMainWindow, QDialog {{ 515 | background-color: {theme["background_color"]}; 516 | color: {theme["text_color"]}; 517 | }} 518 | 519 | QPushButton {{ 520 | background-color: {theme["button_color"]}; 521 | color: {theme["button_text_color"]}; 522 | border: 1px solid {theme["border_color"]}; 523 | border-radius: 4px; 524 | padding: 5px 10px; 525 | }} 526 | 527 | QPushButton:hover {{ 528 | background-color: {theme["button_hover_color"]}; 529 | }} 530 | 531 | QPushButton:pressed {{ 532 | background-color: {theme["secondary_color"]}; 533 | }} 534 | 535 | QTabWidget::pane {{ 536 | border: 1px solid {theme["border_color"]}; 537 | background-color: {theme["background_color"]}; 538 | }} 539 | 540 | QTabBar::tab {{ 541 | background-color: {theme["tab_color"]}; 542 | color: {theme["tab_text_color"]}; 543 | border: 1px solid {theme["border_color"]}; 544 | border-bottom: none; 545 | border-top-left-radius: 4px; 546 | border-top-right-radius: 4px; 547 | padding: 5px 10px; 548 | margin-right: 2px; 549 | }} 550 | 551 | QTabBar::tab:selected {{ 552 | background-color: {theme["tab_selected_color"]}; 553 | color: {theme["tab_selected_text_color"]}; 554 | }} 555 | 556 | QTabBar::tab:hover:!selected {{ 557 | background-color: {theme["button_hover_color"]}; 558 | }} 559 | 560 | QLineEdit, QTextEdit, QPlainTextEdit, QComboBox {{ 561 | border: 1px solid {theme["border_color"]}; 562 | border-radius: 4px; 563 | padding: 3px; 564 | background-color: {theme["background_color"]}; 565 | color: {theme["text_color"]}; 566 | }} 567 | 568 | QLabel {{ 569 | color: {theme["text_color"]}; 570 | }} 571 | 572 | QCheckBox {{ 573 | color: {theme["text_color"]}; 574 | }} 575 | 576 | QRadioButton {{ 577 | color: {theme["text_color"]}; 578 | }} 579 | 580 | QGroupBox {{ 581 | border: 1px solid {theme["border_color"]}; 582 | border-radius: 4px; 583 | margin-top: 10px; 584 | color: {theme["text_color"]}; 585 | }} 586 | 587 | QGroupBox::title {{ 588 | subcontrol-origin: margin; 589 | subcontrol-position: top center; 590 | padding: 0 5px; 591 | }} 592 | 593 | QMenuBar {{ 594 | background-color: {theme["background_color"]}; 595 | color: {theme["text_color"]}; 596 | }} 597 | 598 | QMenuBar::item:selected {{ 599 | background-color: {theme["primary_color"]}; 600 | color: {theme["button_text_color"]}; 601 | }} 602 | 603 | QMenu {{ 604 | background-color: {theme["background_color"]}; 605 | color: {theme["text_color"]}; 606 | border: 1px solid {theme["border_color"]}; 607 | }} 608 | 609 | QMenu::item:selected {{ 610 | background-color: {theme["primary_color"]}; 611 | color: {theme["button_text_color"]}; 612 | }} 613 | 614 | QProgressBar {{ 615 | border: 1px solid {theme["border_color"]}; 616 | border-radius: 4px; 617 | text-align: center; 618 | }} 619 | 620 | QProgressBar::chunk {{ 621 | background-color: {theme["primary_color"]}; 622 | }} 623 | 624 | QScrollBar:vertical {{ 625 | border: 1px solid {theme["border_color"]}; 626 | background: {theme["background_color"]}; 627 | width: 15px; 628 | margin: 15px 0 15px 0; 629 | }} 630 | 631 | QScrollBar::handle:vertical {{ 632 | background: {theme["button_color"]}; 633 | min-height: 20px; 634 | }} 635 | 636 | QScrollBar:horizontal {{ 637 | border: 1px solid {theme["border_color"]}; 638 | background: {theme["background_color"]}; 639 | height: 15px; 640 | margin: 0 15px 0 15px; 641 | }} 642 | 643 | QScrollBar::handle:horizontal {{ 644 | background: {theme["button_color"]}; 645 | min-width: 20px; 646 | }} 647 | """ 648 | 649 | def add_theme(self, name, theme_dict): 650 | """Add a new theme to the theme manager""" 651 | # Validate theme dict has all required keys 652 | required_keys = set(self.themes["default"].keys()) 653 | if not required_keys.issubset(set(theme_dict.keys())): 654 | missing_keys = required_keys - set(theme_dict.keys()) 655 | raise ValueError(f"Theme is missing required keys: {missing_keys}") 656 | 657 | self.themes[name] = theme_dict 658 | return True 659 | 660 | # Create a singleton instance 661 | theme_manager = ThemeManager() -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Team Mizogg 3 | """ 4 | from PyQt6.QtCore import * 5 | from PyQt6.QtWidgets import * 6 | from PyQt6.QtGui import * 7 | import os 8 | import glob 9 | import subprocess 10 | import webbrowser 11 | import signal 12 | import platform 13 | import multiprocessing 14 | from libs.console_gui import ConsoleWindow 15 | from libs.command_thread import CommandThread 16 | from libs.about_dialog import AboutDialog 17 | from libs.progress_dialog import ProgressDialog 18 | from libs.Range_gui import RangeDialog 19 | from libs.theme_manager import ThemeManager 20 | import configparser 21 | 22 | theme_manager = ThemeManager() 23 | ICO_ICON = "images/miz.ico" 24 | TITLE_ICON = "images/mizogglogo.png" 25 | RED_ICON = "images/mizogg-eyes.png" 26 | version = '1.2' 27 | current_platform = platform.system() 28 | 29 | def open_website(self): 30 | webbrowser.open("https://mizogg.co.uk") 31 | 32 | class GUI(QMainWindow): 33 | def __init__(self): 34 | super().__init__() 35 | self.current_instances = 1 36 | self.keyhunt_frames = [] 37 | self.shared_config = None # Will hold shared configuration 38 | self.cpu_count = multiprocessing.cpu_count() # Initialize cpu_count 39 | self.command_threads = {} # Dictionary to store command threads for each instance 40 | self.initUI() 41 | 42 | def initUI(self): 43 | main_widget = QWidget(self) 44 | main_layout = QVBoxLayout(main_widget) 45 | 46 | # Add shared configuration at the top 47 | self.shared_config = self.create_shared_config() 48 | main_layout.addWidget(self.shared_config) 49 | 50 | # Add grid for console windows 51 | self.grid_widget = QWidget(self) 52 | self.grid_layout = QGridLayout(self.grid_widget) 53 | main_layout.addWidget(self.grid_widget) 54 | 55 | main_widget.setLayout(main_layout) 56 | self.setCentralWidget(main_widget) 57 | 58 | # Create menu bar 59 | menubar = self.menuBar() 60 | file_menu = menubar.addMenu("File") 61 | file_menu.addAction("Settings", self.open_settings) 62 | file_menu.addSeparator() 63 | file_menu.addAction("Quit", self.exit_app) 64 | 65 | instances_menu = menubar.addMenu("Instances") 66 | instances_menu.addAction("1", lambda: self.update_grid_layout(1)) 67 | instances_menu.addAction("2", lambda: self.update_grid_layout(2)) 68 | instances_menu.addAction("4", lambda: self.update_grid_layout(4)) 69 | instances_menu.addAction("6", lambda: self.update_grid_layout(6)) 70 | instances_menu.addAction("8", lambda: self.update_grid_layout(8)) 71 | file_menu.addSeparator() 72 | 73 | help_menu = menubar.addMenu("Help") 74 | help_menu.addAction("Help Telegram Group", self.open_telegram) 75 | help_menu.addAction("About", self.about) 76 | 77 | # Add credits 78 | self.add_credits(main_layout) 79 | 80 | self.setGeometry(60, 100, 400, 300) 81 | self.setWindowTitle("KeyHunter Puzzles GUI") 82 | self.setWindowIcon(QIcon(f"{ICO_ICON}")) 83 | self.update_grid_layout(1) 84 | 85 | def create_shared_config(self): 86 | group_box = QGroupBox("Shared Configuration") 87 | layout = QVBoxLayout() 88 | 89 | # Add all configuration controls from KeyHuntFrame 90 | keyhunt_config = self.create_threadGroupBox() 91 | layout.addWidget(keyhunt_config) 92 | 93 | keyspace_config = self.create_keyspaceGroupBox() 94 | layout.addWidget(keyspace_config) 95 | 96 | output_config = self.create_outputFileGroupBox() 97 | layout.addWidget(output_config) 98 | 99 | # Add start/stop buttons 100 | button_layout = QHBoxLayout() 101 | self.start_button = QPushButton("Start All Instances") 102 | self.start_button.clicked.connect(self.start_all_instances) 103 | self.stop_button = QPushButton("Stop All Instances") 104 | self.stop_button.clicked.connect(self.stop_all_instances) 105 | button_layout.addWidget(self.start_button) 106 | button_layout.addWidget(self.stop_button) 107 | layout.addLayout(button_layout) 108 | 109 | group_box.setLayout(layout) 110 | return group_box 111 | 112 | def update_grid_layout(self, num_instances): 113 | self.current_instances = num_instances 114 | self.keyhunt_frames.clear() 115 | 116 | # Clear existing widgets 117 | for i in reversed(range(self.grid_layout.count())): 118 | widget = self.grid_layout.itemAt(i).widget() 119 | if widget is not None: 120 | self.grid_layout.removeWidget(widget) 121 | widget.setParent(None) 122 | 123 | # Set up grid dimensions 124 | if num_instances == 1: 125 | rows, cols = 1, 1 126 | elif num_instances == 2: 127 | rows, cols = 1, 2 128 | elif num_instances == 4: 129 | rows, cols = 2, 2 130 | elif num_instances == 6: 131 | rows, cols = 2, 3 132 | elif num_instances == 8: 133 | rows, cols = 2, 4 134 | 135 | # Calculate max CPUs per instance 136 | max_cpus_per_instance = self.cpu_count // num_instances 137 | if max_cpus_per_instance < 1: 138 | max_cpus_per_instance = 1 139 | 140 | # Update CPU combo box with new limits 141 | self.threadComboBox_key.clear() 142 | for i in range(1, max_cpus_per_instance + 1): 143 | self.threadComboBox_key.addItem(str(i)) 144 | self.threadComboBox_key.setCurrentIndex(0) # Set to first available option 145 | 146 | # Create new console windows 147 | instance_number = 1 148 | for row in range(rows): 149 | for col in range(cols): 150 | console = ConsoleWindow(self) 151 | console.setWindowTitle(f"Instance {instance_number}/{num_instances}") 152 | self.grid_layout.addWidget(console, row, col) 153 | self.keyhunt_frames.append(console) 154 | instance_number += 1 155 | 156 | self.grid_widget.setLayout(self.grid_layout) 157 | self.adjust_size() 158 | 159 | def start_all_instances(self): 160 | try: 161 | # Get the range from shared input 162 | range_text = self.keyspaceLineEdit.text().strip() 163 | if not range_text: 164 | QMessageBox.warning(self, "Error", "Please enter a range") 165 | return 166 | 167 | start_range, end_range = range_text.split(':') 168 | # Convert hex strings to integers, handling both with and without 0x prefix 169 | start_range = int(start_range, 16) if start_range.startswith('0x') else int(start_range, 16) 170 | end_range = int(end_range, 16) if end_range.startswith('0x') else int(end_range, 16) 171 | 172 | # Split range among instances 173 | ranges = self.split_range(start_range, end_range, len(self.keyhunt_frames)) 174 | 175 | # Start each instance with its portion of the range 176 | for i, console in enumerate(self.keyhunt_frames): 177 | instance_start, instance_end = ranges[i] 178 | self.start_instance(console, instance_start, instance_end, i + 1) 179 | 180 | except ValueError as e: 181 | QMessageBox.warning(self, "Error", f"Invalid range format: {str(e)}\nPlease use hex format (e.g., 400000000000000000:7FFFFFFFFFFFFFFFFF)") 182 | 183 | def start_instance(self, console, start_range, end_range, instance_number): 184 | """Start a single instance with the given range""" 185 | console.append_output(f"Instance {instance_number}/{len(self.keyhunt_frames)}") 186 | console.append_output(f"Range: {format(start_range, 'x')} to {format(end_range, 'x')}") 187 | 188 | command = self.construct_command_key(start_range, end_range) 189 | self.execute_command(console, command, instance_number) 190 | 191 | def construct_command_key(self, start_range, end_range): 192 | """Construct keyhunt command with current configuration""" 193 | mode = self.modeComboBox.currentText().strip() 194 | thread_count = int(self.threadComboBox_key.currentText()) 195 | base_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "keyhunt") 196 | 197 | if platform.system() == "Windows": 198 | command = [os.path.join(base_path, "keyhunt.exe"), "-m", mode, "-t", str(thread_count)] 199 | else: 200 | command = [os.path.join(base_path, "keyhunt"), "-m", mode, "-t", str(thread_count)] 201 | 202 | # Add range 203 | command.extend(["-r", f"{format(start_range, 'x')}:{format(end_range, 'x')}"]) 204 | 205 | # Add other parameters from shared configuration 206 | file = self.inputFileLineEdit.text().strip() 207 | if file: 208 | input_file_relative_path = ["input", file] 209 | input_file_path = os.path.join(*input_file_relative_path) 210 | command.extend(["-f", input_file_path]) 211 | 212 | move_mode = self.move_modeEdit.currentText().strip() 213 | if move_mode == 'random': 214 | if mode == 'bsgs': 215 | command.extend(["-B", move_mode]) 216 | else: 217 | command.append("-R") 218 | elif move_mode == 'sequential': 219 | if mode == 'bsgs': 220 | command.extend(["-B", move_mode]) 221 | else: 222 | command.append("-S") 223 | elif move_mode == 'backward' and mode == 'bsgs': 224 | command.extend(["-B", move_mode]) 225 | elif move_mode == 'dance' and mode == 'bsgs': 226 | command.extend(["-B", move_mode]) 227 | elif move_mode == 'both' and mode == 'bsgs': 228 | command.extend(["-B", move_mode]) 229 | 230 | if not (mode == 'bsgs' and move_mode == 'both'): 231 | stride = self.strideLineEdit.text().strip() 232 | if stride: 233 | command.extend(["-I", stride]) 234 | 235 | crypto = self.cryptoComboBox.currentText().strip() 236 | if crypto == "eth": 237 | command.extend(["-c", crypto]) 238 | 239 | look = self.lookComboBox.currentText().strip() 240 | if look and crypto != "eth": # Only add look type if not ETH 241 | command.extend(["-l", look]) 242 | 243 | if mode == 'bsgs': 244 | n_value = self.nValueLineEdit.text().strip() 245 | if n_value: 246 | command.extend(["-n", n_value]) 247 | kamount = int(self.kComboBox.currentText().strip()) 248 | command.extend(["-k", str(kamount)]) 249 | 250 | if self.flagQCheckBox.isChecked(): 251 | command.append("-q") 252 | 253 | return command 254 | 255 | def execute_command(self, console, command, instance_number): 256 | """Execute command and show output in the given console window""" 257 | # Stop existing thread for this instance if it exists 258 | if instance_number in self.command_threads: 259 | self.command_threads[instance_number].terminate() 260 | self.command_threads[instance_number].wait() 261 | 262 | # Convert command list to string for display 263 | command_str = ' '.join(str(x) for x in command) 264 | console.append_output(f"Executing command: {command_str}") 265 | 266 | # Create and start new thread for this instance 267 | thread = CommandThread(command) 268 | thread.commandOutput.connect(console.append_output) 269 | thread.commandFinished.connect(lambda: self.command_finished(console, instance_number)) 270 | thread.start() 271 | 272 | # Store the thread 273 | self.command_threads[instance_number] = thread 274 | 275 | def command_finished(self, console, instance_number): 276 | """Handle command completion for a specific instance""" 277 | if instance_number in self.command_threads: 278 | thread = self.command_threads[instance_number] 279 | if thread and thread.isRunning(): 280 | thread.terminate() 281 | console.append_output("Process stopped by user") 282 | del self.command_threads[instance_number] 283 | 284 | def stop_all_instances(self): 285 | """Stop all running instances""" 286 | try: 287 | for instance_number, thread in list(self.command_threads.items()): 288 | if thread and thread.isRunning(): 289 | # Terminate the thread 290 | thread.terminate() 291 | thread.wait() 292 | 293 | # Kill any remaining keyhunt processes 294 | if platform.system() == "Windows": 295 | try: 296 | subprocess.run(["taskkill", "/f", "/im", "keyhunt.exe"], 297 | stdout=subprocess.DEVNULL, 298 | stderr=subprocess.DEVNULL, 299 | timeout=2) 300 | except subprocess.TimeoutExpired: 301 | pass 302 | except Exception: 303 | pass 304 | else: 305 | try: 306 | subprocess.run(["pkill", "-f", "keyhunt"], 307 | stdout=subprocess.DEVNULL, 308 | stderr=subprocess.DEVNULL, 309 | timeout=2) 310 | except subprocess.TimeoutExpired: 311 | pass 312 | except Exception: 313 | pass 314 | 315 | if instance_number <= len(self.keyhunt_frames): 316 | self.keyhunt_frames[instance_number - 1].append_output("Process stopped by user") 317 | 318 | # Clear the thread dictionary 319 | self.command_threads.clear() 320 | except Exception as e: 321 | print(f"Error during cleanup: {str(e)}") 322 | 323 | def split_range(self, start, end, num_splits): 324 | """Split a range into equal parts""" 325 | total_range = end - start 326 | chunk_size = total_range // num_splits 327 | remainder = total_range % num_splits 328 | 329 | ranges = [] 330 | current_start = start 331 | 332 | for i in range(num_splits): 333 | extra = 1 if i < remainder else 0 334 | current_end = current_start + chunk_size + extra - 1 335 | if i == num_splits - 1: 336 | current_end = end 337 | ranges.append((current_start, current_end)) 338 | current_start = current_end + 1 339 | 340 | return ranges 341 | 342 | def open_settings(self): 343 | from libs.theme_manager import SettingsDialog 344 | settings = SettingsDialog(self) 345 | settings.settings_changed.connect(self.apply_settings_changes) 346 | settings.exec() 347 | 348 | def apply_settings_changes(self): 349 | config = configparser.ConfigParser() 350 | config.read('config.ini') 351 | theme_name = config.get('theme', 'name', fallback='dark') 352 | theme_manager.set_theme(theme_name) 353 | stylesheet = theme_manager.get_stylesheet() 354 | QApplication.instance().setStyleSheet(stylesheet) 355 | for widget in self.findChildren(QWidget): 356 | widget.setStyleSheet(stylesheet) 357 | 358 | def exit_app(self): 359 | QApplication.quit() 360 | 361 | def open_telegram(self): 362 | webbrowser.open("https://t.me/TeamHunter_GUI") 363 | 364 | def about(self): 365 | about_dialog = AboutDialog(self) 366 | about_dialog.show() 367 | 368 | def add_credits(self, layout): 369 | labels_info = [ 370 | {"text": "Made by Team Mizogg", "object_name": "madeby"}, 371 | {"text": f"Full Version {version} ({current_platform})", "object_name": f"{current_platform}_version"}, 372 | {"text": "© mizogg.com 2018 - 2025", "object_name": "copyright"}, 373 | ] 374 | 375 | dot_labels = [QLabel("●", objectName=f"dot{i}") for i in range(1, 3)] 376 | 377 | credit_label = QHBoxLayout() 378 | credit_label.setAlignment(Qt.AlignmentFlag.AlignCenter) 379 | icon_size = QSize(26, 26) 380 | iconred = QIcon(QPixmap(RED_ICON)) 381 | 382 | def create_miz_git_mode_button(): 383 | button = QPushButton(self) 384 | button.setToolTip('Help ME. Just by visiting my site https://mizogg.co.uk keep up those clicks. Mizogg Website and Information ') 385 | button.setStyleSheet("font-size: 12pt;") 386 | button.setIconSize(icon_size) 387 | button.setIcon(iconred) 388 | button.clicked.connect(open_website) 389 | return button 390 | 391 | self.miz_git_mode_button = create_miz_git_mode_button() 392 | self.miz_git_mode_button1 = create_miz_git_mode_button() 393 | 394 | credit_label.addWidget(self.miz_git_mode_button) 395 | 396 | mizlogo = QPixmap(f"{TITLE_ICON}") 397 | miz_label = QLabel(self) 398 | miz_label.setPixmap(mizlogo) 399 | miz_label1 = QLabel(self) 400 | miz_label1.setPixmap(mizlogo) 401 | 402 | credit_label.addWidget(miz_label) 403 | 404 | for info in labels_info: 405 | label = QLabel(info["text"]) 406 | credit_label.addWidget(label) 407 | if dot_labels: 408 | dot_label = dot_labels.pop(0) 409 | credit_label.addWidget(dot_label) 410 | 411 | credit_label.addWidget(miz_label1) 412 | credit_label.addWidget(self.miz_git_mode_button1) 413 | layout.addLayout(credit_label) 414 | 415 | def create_threadGroupBox(self): 416 | self.keyhuntLayout = QVBoxLayout() 417 | threadGroupBox = QGroupBox(self) 418 | threadGroupBox.setTitle("Key Hunt Configuration") 419 | threadGroupBox.setStyleSheet("QGroupBox { border: 3px solid; padding: 15px; }") 420 | self.row1Layout = QHBoxLayout() 421 | self.threadLabel = QLabel("Number of CPUs:", self) 422 | self.row1Layout.addWidget(self.threadLabel) 423 | self.threadComboBox_key = QComboBox() 424 | 425 | # Initial CPU options based on current instances 426 | max_cpus_per_instance = self.cpu_count // self.current_instances 427 | if max_cpus_per_instance < 1: 428 | max_cpus_per_instance = 1 429 | 430 | for i in range(1, max_cpus_per_instance + 1): 431 | self.threadComboBox_key.addItem(str(i)) 432 | 433 | self.threadComboBox_key.setCurrentIndex(0) 434 | self.threadComboBox_key.setToolTip(f' Maximum CPUs per instance: {max_cpus_per_instance} (Total CPUs: {self.cpu_count})') 435 | self.row1Layout.setStretchFactor(self.threadComboBox_key, 1) 436 | self.row1Layout.addWidget(self.threadComboBox_key) 437 | 438 | self.cryptoLabel = QLabel("Crypto:", self) 439 | self.row1Layout.addWidget(self.cryptoLabel) 440 | self.cryptoComboBox = QComboBox() 441 | self.cryptoComboBox.addItem("btc") 442 | self.cryptoComboBox.addItem("eth") 443 | self.cryptoComboBox.setToolTip(' Crypto Scanning Type BTC or ETH (default BTC)') 444 | self.cryptoComboBox.currentIndexChanged.connect(self.update_look_type_options) 445 | self.row1Layout.addWidget(self.cryptoComboBox) 446 | 447 | self.modeLabel = QLabel("Mode:", self) 448 | self.row1Layout.addWidget(self.modeLabel) 449 | self.modeComboBox = QComboBox() 450 | self.modeComboBox.addItem("address") 451 | self.modeComboBox.addItem("bsgs") 452 | self.modeComboBox.addItem("rmd160") 453 | self.modeComboBox.setToolTip(' Keyhunt can work in diferent ways at different speeds. The current availables modes are:') 454 | self.row1Layout.addWidget(self.modeComboBox) 455 | self.move_modeLabel = QLabel("Movement Mode:", self) 456 | self.row1Layout.addWidget(self.move_modeLabel) 457 | self.move_modeEdit = QComboBox(self) 458 | self.move_modeEdit.addItem("random") 459 | self.move_modeEdit.addItem("sequential") 460 | self.move_modeEdit.setToolTip(' Direction of Scan ') 461 | self.row1Layout.addWidget(self.move_modeEdit) 462 | self.modeComboBox.currentIndexChanged.connect(self.update_movement_mode_options) 463 | self.strideLabel = QLabel("Stride/Jump/Magnitude:", self) 464 | self.row1Layout.addWidget(self.strideLabel) 465 | self.strideLineEdit = QLineEdit("1") 466 | self.strideLineEdit.setPlaceholderText('10000') 467 | self.strideLineEdit.setToolTip(' Increment by NUMBER (Not required BSGS Mode both)') 468 | self.row1Layout.addWidget(self.strideLineEdit) 469 | self.kLabel = QLabel(" K factor:", self) 470 | self.kLabel.setToolTip('BSGS Modes only') 471 | self.row1Layout.addWidget(self.kLabel) 472 | self.kComboBox = QComboBox() 473 | self.kComboBox.addItems(['1', '4', '8', '16', '24', '32', '64', '128', '256', '512', '756', '1024', '2048']) 474 | self.kComboBox.setToolTip('1, 32, 64, 128, 256, 512, 1024') 475 | self.row1Layout.addWidget(self.kComboBox) 476 | self.keyhuntLayout.addLayout(self.row1Layout) 477 | self.nValueLabel = QLabel("N Value:", self) 478 | self.row1Layout.addWidget(self.nValueLabel) 479 | self.nValueLineEdit = QLineEdit(self) 480 | self.nValueLineEdit.setPlaceholderText('0x1000000000000000') 481 | self.row1Layout.addWidget(self.nValueLineEdit) 482 | self.row1Layout.setStretchFactor(self.nValueLineEdit, 2) 483 | 484 | threadGroupBox.setLayout(self.keyhuntLayout) 485 | return threadGroupBox 486 | 487 | def create_keyspaceGroupBox(self): 488 | keyspaceGroupBox = QGroupBox(self) 489 | keyspaceGroupBox.setTitle("Key Space Configuration") 490 | keyspaceGroupBox.setStyleSheet("QGroupBox { border: 3px solid; padding: 5px; }") 491 | keyspaceMainLayout = QVBoxLayout(keyspaceGroupBox) 492 | keyspaceLayout = QHBoxLayout() 493 | keyspaceLabel = QLabel("Key Space:") 494 | keyspaceLayout.addWidget(keyspaceLabel) 495 | self.keyspaceLineEdit = QLineEdit("400000000000000000:7FFFFFFFFFFFFFFFFF") 496 | self.keyspaceLineEdit.setToolTip(' Type in your own HEX Range separated with : ') 497 | keyspaceLayout.addWidget(self.keyspaceLineEdit) 498 | keyspaceMainLayout.addLayout(keyspaceLayout) 499 | keyspacerange_layout = QHBoxLayout() 500 | self.keyspace_slider = QSlider(Qt.Orientation.Horizontal) 501 | self.keyspace_slider.setMinimum(1) 502 | self.keyspace_slider.setMaximum(256) 503 | self.keyspace_slider.setValue(71) 504 | self.keyspace_slider.setToolTip(' Drag Left to Right to Adjust Range (Address Mode 1-160 BSGS Mode 50-160)') 505 | keyspacerange_layout1 = QHBoxLayout() 506 | keyspacerange_layout1.addWidget(self.keyspace_slider) 507 | self.keyspace_slider.valueChanged.connect(self.update_keyspace_range) 508 | self.bitsLabel = QLabel("Bits:", self) 509 | self.bitsLineEdit = QLineEdit(self) 510 | self.bitsLineEdit.setText("71") 511 | self.bitsLineEdit.textChanged.connect(self.updateSliderAndRanges) 512 | keyspacerange_layout1.addWidget(self.bitsLabel) 513 | keyspacerange_layout1.addWidget(self.bitsLineEdit) 514 | keyspaceMainLayout.addLayout(keyspacerange_layout) 515 | keyspaceMainLayout.addLayout(keyspacerange_layout1) 516 | return keyspaceGroupBox 517 | 518 | def update_keyspace_range(self, value): 519 | start_range = 2 ** (value - 1) 520 | end_range = 2 ** value - 1 521 | self.keyspaceLineEdit.setText(f"{start_range:X}:{end_range:X}") 522 | self.bitsLineEdit.setText(str(value)) 523 | 524 | def updateSliderAndRanges(self, text): 525 | try: 526 | bits = int(text) 527 | mode = self.modeComboBox.currentText() 528 | if mode == "bsgs": 529 | bits = max(50, min(bits, 256)) 530 | else: 531 | bits = max(1, min(bits, 256)) 532 | 533 | if bits == 256: 534 | start_range = "8000000000000000000000000000000000000000000000000000000000000000" 535 | end_range = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140" 536 | else: 537 | start_range = 2 ** (bits - 1) 538 | end_range = 2 ** bits - 1 539 | start_range = f"{start_range:X}" 540 | end_range = f"{end_range:X}" 541 | 542 | self.keyspace_slider.setValue(bits) 543 | self.keyspaceLineEdit.setText(f"{start_range}:{end_range}") 544 | 545 | except ValueError: 546 | range_message = "Range should be in Bit 1-256" 547 | QMessageBox.information(self, "Range Error", range_message) 548 | 549 | def range_check(self): 550 | self.range_dialog = RangeDialog() 551 | self.range_dialog.show() 552 | 553 | def create_outputFileGroupBox(self): 554 | outputFileGroupBox = QGroupBox(self) 555 | outputFileGroupBox.setTitle("File Configuration and Look Type (Compressed/Uncompressed)") 556 | outputFileGroupBox.setStyleSheet("QGroupBox { border: 3px solid; padding: 5px; }") 557 | outputFileLayout = QHBoxLayout(outputFileGroupBox) 558 | self.lookLabel = QLabel("Look Type:", self) 559 | outputFileLayout.addWidget(self.lookLabel) 560 | self.lookComboBox = QComboBox() 561 | self.lookComboBox.addItem("compress") 562 | self.lookComboBox.addItem("uncompress") 563 | self.lookComboBox.addItem("both") 564 | self.lookComboBox.setToolTip(' Search for compressed keys (default). Can be used with also search uncompressed keys ') 565 | outputFileLayout.addWidget(self.lookComboBox) 566 | self.inputFileLabel = QLabel("Input File:", self) 567 | outputFileLayout.addWidget(self.inputFileLabel) 568 | self.inputFileLineEdit = QLineEdit("btc.txt", self) 569 | self.inputFileLineEdit.setPlaceholderText('Click browse to find your BTC database') 570 | self.inputFileLineEdit.setToolTip(' Type the Name of database txt file or Browse location ') 571 | outputFileLayout.addWidget(self.inputFileLineEdit) 572 | self.inputFileButton = QPushButton("Browse", self) 573 | self.inputFileButton.setStyleSheet("") 574 | self.inputFileButton.clicked.connect(self.browse_input_file) 575 | self.inputFileButton.setToolTip(' Type the Name of database txt file or Browse location ') 576 | outputFileLayout.addWidget(self.inputFileButton) 577 | self.found_progButton = QPushButton("🔥 Check if Found 🔥") 578 | self.found_progButton.clicked.connect(self.found_prog) 579 | self.found_progButton.setToolTip(' Click Here to See if your a Winner ') 580 | outputFileLayout.addWidget(self.found_progButton) 581 | self.save_progButton = QPushButton("💾 Check Progress 💾") 582 | self.save_progButton.clicked.connect(self.check_prog) 583 | self.save_progButton.setToolTip(' Check if the Progress file exists Choose to Keep or Remove ') 584 | outputFileLayout.addWidget(self.save_progButton) 585 | self.flagQCheckBox = QCheckBox("Quite mode", self) 586 | self.flagQCheckBox.setToolTip(' Quiet the thread output Only Displays speed ') 587 | outputFileLayout.addWidget(self.flagQCheckBox) 588 | self.range_progButton = QPushButton("💾 Range Tools 💾") 589 | self.range_progButton.clicked.connect(self.range_check) 590 | self.range_progButton.setToolTip(' Ranges ....... ') 591 | outputFileLayout.addWidget(self.range_progButton) 592 | return outputFileGroupBox 593 | 594 | def update_kComboBox_status(self): 595 | mode = self.modeComboBox.currentText() 596 | if mode == "address" or mode == "rmd160": 597 | self.kComboBox.setDisabled(True) 598 | self.kComboBox.setStyleSheet("") 599 | self.nValueLineEdit.setDisabled(True) 600 | self.nValueLineEdit.setStyleSheet("") 601 | else: 602 | self.kComboBox.setEnabled(True) 603 | self.kComboBox.setStyleSheet("") 604 | self.nValueLineEdit.setEnabled(True) 605 | self.nValueLineEdit.setStyleSheet("") 606 | 607 | def update_movement_mode_options(self): 608 | mode = self.modeComboBox.currentText() 609 | if mode == "bsgs": 610 | self.move_modeEdit.clear() 611 | self.move_modeEdit.addItem("sequential") 612 | self.move_modeEdit.addItem("backward") 613 | self.move_modeEdit.addItem("both") 614 | self.move_modeEdit.addItem("random") 615 | self.move_modeEdit.addItem("dance") 616 | self.keyspace_slider.setMinimum(50) 617 | else: 618 | self.move_modeEdit.clear() 619 | self.move_modeEdit.addItem("random") 620 | self.move_modeEdit.addItem("sequential") 621 | self.keyspace_slider.setMinimum(1) 622 | 623 | def browse_input_file(self): 624 | file_dialog = QFileDialog(self) 625 | file_dialog.setFileMode(QFileDialog.FileMode.ExistingFile) 626 | file_dialog.setNameFilter("Text Files (*.txt);;Binary Files (*.bin);;All Files (*.*)") 627 | if file_dialog.exec(): 628 | file_path = file_dialog.selectedFiles()[0] 629 | file_name = os.path.basename(file_path) 630 | self.inputFileLineEdit.setText(file_name) 631 | 632 | def found_prog(self): 633 | file_path = 'KEYFOUNDKEYFOUND.txt' 634 | self.read_and_display_file(file_path, "😀😀 Keyhunt File found. Check for Winners 😀😀.", "😞😞No Winners Yet 😞😞") 635 | 636 | def read_and_display_file(self, file_path, success_message, error_message): 637 | for console in self.keyhunt_frames: 638 | console.append_output(f"Attempting to read file: {file_path}") 639 | try: 640 | if not os.path.exists(file_path): 641 | console.append_output(f"⚠️ {error_message} File not found. Please check the file path.") 642 | return None 643 | 644 | with open(file_path, 'r') as file: 645 | output_from_text = file.read() 646 | console.append_output(success_message) 647 | console.append_output(output_from_text) 648 | return output_from_text 649 | except FileNotFoundError: 650 | console.append_output(f"⚠️ {error_message} File not found. Please check the file path.") 651 | return None 652 | except Exception as e: 653 | console.append_output(f"An error occurred: {str(e)}") 654 | return None 655 | 656 | def check_prog(self): 657 | directory = '.' 658 | dat_files = glob.glob(os.path.join(directory, '*.dat')) 659 | 660 | if dat_files: 661 | file_path = dat_files[0] 662 | custom_dialog = ProgressDialog(self) 663 | choice = custom_dialog.exec() 664 | if choice == QDialog.DialogCode.Accepted: 665 | os.remove(file_path) 666 | for console in self.keyhunt_frames: 667 | console.append_output("Progress deleted successfully.") 668 | else: 669 | for console in self.keyhunt_frames: 670 | console.append_output("Progress kept.") 671 | else: 672 | for console in self.keyhunt_frames: 673 | console.append_output("Progress not found.") 674 | 675 | def adjust_size(self): 676 | """Adjust the window size based on the grid layout""" 677 | grid_size = self.grid_widget.sizeHint() 678 | # Add some padding for the shared configuration 679 | config_height = self.shared_config.sizeHint().height() 680 | self.resize(max(800, grid_size.width()), max(600, grid_size.height() + config_height + 100)) 681 | 682 | def closeEvent(self, event): 683 | """Handle window close event""" 684 | try: 685 | # Stop all instances first 686 | self.stop_all_instances() 687 | 688 | # Additional cleanup 689 | if platform.system() == "Windows": 690 | try: 691 | subprocess.run(["taskkill", "/f", "/im", "keyhunt.exe"], 692 | stdout=subprocess.DEVNULL, 693 | stderr=subprocess.DEVNULL, 694 | timeout=2) 695 | except subprocess.TimeoutExpired: 696 | pass 697 | except Exception: 698 | pass 699 | else: 700 | try: 701 | subprocess.run(["pkill", "-f", "keyhunt"], 702 | stdout=subprocess.DEVNULL, 703 | stderr=subprocess.DEVNULL, 704 | timeout=2) 705 | except subprocess.TimeoutExpired: 706 | pass 707 | except Exception: 708 | pass 709 | 710 | event.accept() 711 | except Exception as e: 712 | print(f"Error during window close: {str(e)}") 713 | event.accept() 714 | 715 | def __del__(self): 716 | """Destructor to ensure cleanup""" 717 | try: 718 | self.stop_all_instances() 719 | except: 720 | pass 721 | 722 | def update_look_type_options(self): 723 | """Update look type options based on selected cryptocurrency""" 724 | crypto = self.cryptoComboBox.currentText() 725 | if crypto == "eth": 726 | self.lookComboBox.setDisabled(True) 727 | self.lookComboBox.setStyleSheet("QComboBox { color: gray; }") 728 | else: 729 | self.lookComboBox.setEnabled(True) 730 | self.lookComboBox.setStyleSheet("") 731 | 732 | 733 | if __name__ == "__main__": 734 | app = QApplication([]) 735 | config = configparser.ConfigParser() 736 | 737 | # Check if config.ini exists, if not create it with default settings 738 | if not os.path.exists('config.ini'): 739 | config['theme'] = {'name': 'dark'} 740 | with open('config.ini', 'w') as configfile: 741 | config.write(configfile) 742 | 743 | config.read('config.ini') 744 | theme_name = config.get('theme', 'name', fallback='dark') 745 | theme_manager.set_theme(theme_name) 746 | stylesheet = theme_manager.get_stylesheet() 747 | app.setStyleSheet(stylesheet) 748 | window = GUI() 749 | window.show() 750 | app.exec() -------------------------------------------------------------------------------- /tkmain.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Team Mizogg 3 | """ 4 | import tkinter as tk 5 | from tkinter import ttk, messagebox, filedialog, simpledialog, colorchooser 6 | import os 7 | import glob 8 | import subprocess 9 | import webbrowser 10 | import signal 11 | import platform 12 | import multiprocessing 13 | import threading 14 | import queue 15 | import configparser 16 | from datetime import datetime 17 | 18 | class ConsoleWindow(ttk.Frame): 19 | def __init__(self, parent, title="Console"): 20 | super().__init__(parent) 21 | self.title = title 22 | self.output_queue = queue.Queue() 23 | self.setup_ui() 24 | self.after(100, self.check_queue) 25 | 26 | def setup_ui(self): 27 | # Create text widget with scrollbar 28 | self.text = tk.Text(self, wrap=tk.WORD, height=10, width=50, bg='black', fg='white') 29 | scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.text.yview) 30 | self.text.configure(yscrollcommand=scrollbar.set) 31 | 32 | # Pack widgets 33 | self.text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) 34 | scrollbar.pack(side=tk.RIGHT, fill=tk.Y) 35 | 36 | def append_output(self, text): 37 | self.output_queue.put(text) 38 | 39 | def check_queue(self): 40 | try: 41 | while True: 42 | text = self.output_queue.get_nowait() 43 | self.text.insert(tk.END, text + "\n") 44 | self.text.see(tk.END) 45 | self.text.update_idletasks() 46 | except queue.Empty: 47 | pass 48 | finally: 49 | self.after(100, self.check_queue) 50 | 51 | class CommandThread(threading.Thread): 52 | def __init__(self, command, console): 53 | super().__init__() 54 | self.command = command 55 | self.console = console 56 | self.process = None 57 | self.daemon = True 58 | self.running = True 59 | 60 | def run(self): 61 | try: 62 | self.process = subprocess.Popen( 63 | self.command, 64 | stdout=subprocess.PIPE, 65 | stderr=subprocess.STDOUT, 66 | universal_newlines=True, 67 | bufsize=1, 68 | creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0 69 | ) 70 | 71 | while self.running: 72 | line = self.process.stdout.readline() 73 | if not line: 74 | break 75 | self.console.append_output(line.strip()) 76 | 77 | except Exception as e: 78 | self.console.append_output(f"Error: {str(e)}") 79 | finally: 80 | if self.process: 81 | self.process.stdout.close() 82 | self.process.wait() 83 | 84 | def terminate(self): 85 | self.running = False 86 | if self.process: 87 | if platform.system() == "Windows": 88 | subprocess.run(["taskkill", "/f", "/t", "/pid", str(self.process.pid)], 89 | stdout=subprocess.DEVNULL, 90 | stderr=subprocess.DEVNULL) 91 | else: 92 | self.process.terminate() 93 | 94 | class KeyHunterGUI: 95 | def __init__(self, root): 96 | self.root = root 97 | self.root.title("KeyHunter Puzzles TKinter GUI ") 98 | self.current_instances = 1 99 | self.console_frames = [] 100 | self.command_threads = {} 101 | self.cpu_count = multiprocessing.cpu_count() 102 | 103 | # Set theme 104 | self.style = ttk.Style() 105 | self.style.theme_use('clam') 106 | 107 | self.setup_ui() 108 | self.load_config() 109 | 110 | def setup_ui(self): 111 | # Create main container 112 | self.main_container = ttk.Frame(self.root, padding="5") 113 | self.main_container.pack(fill=tk.BOTH, expand=True) 114 | 115 | # Create menu 116 | self.create_menu() 117 | 118 | # Create shared configuration 119 | self.create_shared_config() 120 | 121 | # Create console grid 122 | self.console_grid = ttk.Frame(self.main_container) 123 | self.console_grid.pack(fill=tk.BOTH, expand=True, pady=5) 124 | 125 | # Create initial console 126 | self.update_grid_layout(1) 127 | 128 | # Add credits 129 | self.add_credits() 130 | 131 | def create_menu(self): 132 | menubar = tk.Menu(self.root) 133 | 134 | # File menu 135 | file_menu = tk.Menu(menubar, tearoff=0) 136 | file_menu.add_command(label="Settings", command=self.open_settings) 137 | file_menu.add_separator() 138 | file_menu.add_command(label="Quit", command=self.root.quit) 139 | menubar.add_cascade(label="File", menu=file_menu) 140 | 141 | # Instances menu 142 | instances_menu = tk.Menu(menubar, tearoff=0) 143 | for num in [1, 2, 4, 6, 8]: 144 | instances_menu.add_command( 145 | label=str(num), 146 | command=lambda n=num: self.update_grid_layout(n) 147 | ) 148 | menubar.add_cascade(label="Instances", menu=instances_menu) 149 | 150 | # Help menu 151 | help_menu = tk.Menu(menubar, tearoff=0) 152 | help_menu.add_command(label="Help Telegram Group", command=self.open_telegram) 153 | help_menu.add_command(label="About", command=self.about) 154 | menubar.add_cascade(label="Help", menu=help_menu) 155 | 156 | self.root.config(menu=menubar) 157 | 158 | def create_shared_config(self): 159 | config_frame = ttk.LabelFrame(self.main_container, text="Shared Configuration", padding="5") 160 | config_frame.pack(fill=tk.X, pady=5) 161 | 162 | # Key Hunt Configuration 163 | hunt_frame = ttk.LabelFrame(config_frame, text="Key Hunt Configuration", padding="5") 164 | hunt_frame.pack(fill=tk.X, pady=5) 165 | 166 | # Row 1 167 | row1 = ttk.Frame(hunt_frame) 168 | row1.pack(fill=tk.X, pady=2) 169 | 170 | # CPU Count 171 | ttk.Label(row1, text="Number of CPUs:").pack(side=tk.LEFT, padx=5) 172 | self.thread_combo = ttk.Combobox(row1, width=5) 173 | self.thread_combo.pack(side=tk.LEFT, padx=5) 174 | self.update_cpu_options() 175 | 176 | # Crypto Type 177 | ttk.Label(row1, text="Crypto:").pack(side=tk.LEFT, padx=5) 178 | self.crypto_combo = ttk.Combobox(row1, values=["btc", "eth"], width=5) 179 | self.crypto_combo.set("btc") 180 | self.crypto_combo.pack(side=tk.LEFT, padx=5) 181 | self.crypto_combo.bind('<>', self.update_look_type_options) 182 | 183 | # Mode 184 | ttk.Label(row1, text="Mode:").pack(side=tk.LEFT, padx=5) 185 | self.mode_combo = ttk.Combobox(row1, values=["address", "bsgs", "rmd160"], width=10) 186 | self.mode_combo.set("address") 187 | self.mode_combo.pack(side=tk.LEFT, padx=5) 188 | self.mode_combo.bind('<>', self.update_movement_mode_options) 189 | 190 | # Movement Mode 191 | ttk.Label(row1, text="Movement Mode:").pack(side=tk.LEFT, padx=5) 192 | self.move_mode_combo = ttk.Combobox(row1, values=["random", "sequential"], width=10) 193 | self.move_mode_combo.set("random") 194 | self.move_mode_combo.pack(side=tk.LEFT, padx=5) 195 | 196 | # Stride 197 | ttk.Label(row1, text="Stride:").pack(side=tk.LEFT, padx=5) 198 | self.stride_entry = ttk.Entry(row1, width=10) 199 | self.stride_entry.insert(0, "1") 200 | self.stride_entry.pack(side=tk.LEFT, padx=5) 201 | 202 | # K factor 203 | ttk.Label(row1, text="K factor:").pack(side=tk.LEFT, padx=5) 204 | self.k_combo = ttk.Combobox(row1, values=['1', '4', '8', '16', '24', '32', '64', '128', '256', '512', '756', '1024', '2048'], width=5) 205 | self.k_combo.set("1") 206 | self.k_combo.pack(side=tk.LEFT, padx=5) 207 | 208 | # N Value 209 | ttk.Label(row1, text="N Value:").pack(side=tk.LEFT, padx=5) 210 | self.n_value_entry = ttk.Entry(row1, width=20) 211 | self.n_value_entry.insert(0, "0x1000000000000000") 212 | self.n_value_entry.pack(side=tk.LEFT, padx=5) 213 | 214 | # Key Space Configuration 215 | keyspace_frame = ttk.LabelFrame(config_frame, text="Key Space Configuration", padding="5") 216 | keyspace_frame.pack(fill=tk.X, pady=5) 217 | 218 | # Key Space 219 | ttk.Label(keyspace_frame, text="Key Space:").pack(side=tk.LEFT, padx=5) 220 | self.keyspace_entry = ttk.Entry(keyspace_frame, width=50) 221 | self.keyspace_entry.insert(0, "400000000000000000:7FFFFFFFFFFFFFFFFF") 222 | self.keyspace_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) 223 | 224 | # Bits slider and entry 225 | bits_frame = ttk.Frame(keyspace_frame) 226 | bits_frame.pack(fill=tk.X, pady=5) 227 | ttk.Label(bits_frame, text="Bits:").pack(side=tk.LEFT, padx=5) 228 | 229 | # Create bits_entry first 230 | self.bits_entry = ttk.Entry(bits_frame, width=5) 231 | self.bits_entry.insert(0, "71") 232 | self.bits_entry.pack(side=tk.RIGHT, padx=5) 233 | self.bits_entry.bind('', lambda e: self.updateSliderAndRanges()) 234 | 235 | # Then create bits_slider 236 | self.bits_slider = ttk.Scale(bits_frame, from_=1, to=256, orient=tk.HORIZONTAL, command=self.update_keyspace_range) 237 | self.bits_slider.set(71) 238 | self.bits_slider.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) 239 | 240 | # File Configuration 241 | file_frame = ttk.LabelFrame(config_frame, text="File Configuration", padding="5") 242 | file_frame.pack(fill=tk.X, pady=5) 243 | 244 | # Look Type 245 | ttk.Label(file_frame, text="Look Type:").pack(side=tk.LEFT, padx=5) 246 | self.look_combo = ttk.Combobox(file_frame, values=["compress", "uncompress", "both"], width=10) 247 | self.look_combo.set("compress") 248 | self.look_combo.pack(side=tk.LEFT, padx=5) 249 | 250 | # Input File 251 | ttk.Label(file_frame, text="Input File:").pack(side=tk.LEFT, padx=5) 252 | self.input_file_entry = ttk.Entry(file_frame, width=20) 253 | self.input_file_entry.insert(0, "btc.txt") 254 | self.input_file_entry.pack(side=tk.LEFT, padx=5) 255 | ttk.Button(file_frame, text="Browse", command=self.browse_input_file).pack(side=tk.LEFT, padx=5) 256 | 257 | # Buttons 258 | button_frame = ttk.Frame(config_frame) 259 | button_frame.pack(fill=tk.X, pady=5) 260 | 261 | ttk.Button(button_frame, text="Start All Instances", command=self.start_all_instances).pack(side=tk.LEFT, padx=5) 262 | ttk.Button(button_frame, text="Stop All Instances", command=self.stop_all_instances).pack(side=tk.LEFT, padx=5) 263 | ttk.Button(button_frame, text="🔥 Check if Found 🔥", command=self.found_prog).pack(side=tk.LEFT, padx=5) 264 | ttk.Button(button_frame, text="💾 Check Progress 💾", command=self.check_prog).pack(side=tk.LEFT, padx=5) 265 | ttk.Button(button_frame, text="💾 Range Tools 💾", command=self.range_check).pack(side=tk.LEFT, padx=5) 266 | 267 | # Quiet mode checkbox 268 | self.quiet_var = tk.BooleanVar() 269 | ttk.Checkbutton(button_frame, text="Quiet mode", variable=self.quiet_var).pack(side=tk.LEFT, padx=5) 270 | 271 | def update_cpu_options(self): 272 | max_cpus = self.cpu_count // self.current_instances 273 | if max_cpus < 1: 274 | max_cpus = 1 275 | self.thread_combo['values'] = [str(i) for i in range(1, max_cpus + 1)] 276 | self.thread_combo.set("1") 277 | 278 | def update_grid_layout(self, num_instances): 279 | self.current_instances = num_instances 280 | self.console_frames.clear() 281 | 282 | # Clear existing consoles 283 | for widget in self.console_grid.winfo_children(): 284 | widget.destroy() 285 | 286 | # Calculate grid dimensions 287 | if num_instances == 1: 288 | rows, cols = 1, 1 289 | elif num_instances == 2: 290 | rows, cols = 1, 2 291 | elif num_instances == 4: 292 | rows, cols = 2, 2 293 | elif num_instances == 6: 294 | rows, cols = 2, 3 295 | elif num_instances == 8: 296 | rows, cols = 2, 4 297 | 298 | # Create new consoles 299 | instance_number = 1 300 | for row in range(rows): 301 | for col in range(cols): 302 | console = ConsoleWindow(self.console_grid, f"Instance {instance_number}/{num_instances}") 303 | console.grid(row=row, column=col, sticky="nsew", padx=5, pady=5) 304 | self.console_frames.append(console) 305 | instance_number += 1 306 | 307 | # Update grid weights 308 | for i in range(rows): 309 | self.console_grid.grid_rowconfigure(i, weight=1) 310 | for i in range(cols): 311 | self.console_grid.grid_columnconfigure(i, weight=1) 312 | 313 | # Update CPU options 314 | self.update_cpu_options() 315 | 316 | def update_look_type_options(self, event=None): 317 | crypto = self.crypto_combo.get() 318 | if crypto == "eth": 319 | self.look_combo.configure(state="disabled") 320 | else: 321 | self.look_combo.configure(state="normal") 322 | 323 | def update_movement_mode_options(self, event=None): 324 | mode = self.mode_combo.get() 325 | if mode == "bsgs": 326 | self.move_mode_combo['values'] = ["sequential", "backward", "both", "random", "dance"] 327 | self.bits_slider.configure(from_=50) 328 | else: 329 | self.move_mode_combo['values'] = ["random", "sequential"] 330 | self.bits_slider.configure(from_=1) 331 | self.move_mode_combo.set(self.move_mode_combo['values'][0]) 332 | 333 | def update_keyspace_range(self, value): 334 | try: 335 | bits = int(float(value)) 336 | start_range = 2 ** (bits - 1) 337 | end_range = 2 ** bits - 1 338 | self.keyspace_entry.delete(0, tk.END) 339 | self.keyspace_entry.insert(0, f"{start_range:X}:{end_range:X}") 340 | self.bits_entry.delete(0, tk.END) 341 | self.bits_entry.insert(0, str(bits)) 342 | except ValueError: 343 | pass 344 | 345 | def updateSliderAndRanges(self): 346 | try: 347 | bits = int(self.bits_entry.get()) 348 | mode = self.mode_combo.get() 349 | if mode == "bsgs": 350 | bits = max(50, min(bits, 256)) 351 | else: 352 | bits = max(1, min(bits, 256)) 353 | 354 | if bits == 256: 355 | start_range = "8000000000000000000000000000000000000000000000000000000000000000" 356 | end_range = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140" 357 | else: 358 | start_range = 2 ** (bits - 1) 359 | end_range = 2 ** bits - 1 360 | start_range = f"{start_range:X}" 361 | end_range = f"{end_range:X}" 362 | 363 | self.bits_slider.set(bits) 364 | self.keyspace_entry.delete(0, tk.END) 365 | self.keyspace_entry.insert(0, f"{start_range}:{end_range}") 366 | 367 | except ValueError: 368 | messagebox.showinfo("Range Error", "Range should be in Bit 1-256") 369 | 370 | def construct_command_key(self, start_range, end_range): 371 | """Construct keyhunt command with current configuration""" 372 | mode = self.mode_combo.get() 373 | thread_count = self.thread_combo.get() 374 | base_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "keyhunt") 375 | 376 | if platform.system() == "Windows": 377 | command = [os.path.join(base_path, "keyhunt.exe"), "-m", mode, "-t", thread_count] 378 | else: 379 | command = [os.path.join(base_path, "keyhunt"), "-m", mode, "-t", thread_count] 380 | 381 | # Add range 382 | command.extend(["-r", f"{format(start_range, 'x')}:{format(end_range, 'x')}"]) 383 | 384 | # Add other parameters 385 | file = self.input_file_entry.get().strip() 386 | if file: 387 | input_file_relative_path = ["input", file] 388 | input_file_path = os.path.join(*input_file_relative_path) 389 | command.extend(["-f", input_file_path]) 390 | 391 | move_mode = self.move_mode_combo.get() 392 | if move_mode == 'random': 393 | if mode == 'bsgs': 394 | command.extend(["-B", move_mode]) 395 | else: 396 | command.append("-R") 397 | elif move_mode == 'sequential': 398 | if mode == 'bsgs': 399 | command.extend(["-B", move_mode]) 400 | else: 401 | command.append("-S") 402 | elif move_mode == 'backward' and mode == 'bsgs': 403 | command.extend(["-B", move_mode]) 404 | elif move_mode == 'dance' and mode == 'bsgs': 405 | command.extend(["-B", move_mode]) 406 | elif move_mode == 'both' and mode == 'bsgs': 407 | command.extend(["-B", move_mode]) 408 | 409 | if not (mode == 'bsgs' and move_mode == 'both'): 410 | stride = self.stride_entry.get().strip() 411 | if stride: 412 | command.extend(["-I", stride]) 413 | 414 | crypto = self.crypto_combo.get() 415 | if crypto == "eth": 416 | command.extend(["-c", crypto]) 417 | 418 | look = self.look_combo.get() 419 | if look and crypto != "eth": # Only add look type if not ETH 420 | command.extend(["-l", look]) 421 | 422 | if mode == 'bsgs': 423 | n_value = self.n_value_entry.get().strip() 424 | if n_value: 425 | command.extend(["-n", n_value]) 426 | kamount = self.k_combo.get() 427 | command.extend(["-k", kamount]) 428 | 429 | if self.quiet_var.get(): 430 | command.append("-q") 431 | 432 | return command 433 | 434 | def start_all_instances(self): 435 | try: 436 | # Get the range from shared input 437 | range_text = self.keyspace_entry.get().strip() 438 | if not range_text: 439 | messagebox.showwarning("Error", "Please enter a range") 440 | return 441 | 442 | start_range, end_range = range_text.split(':') 443 | # Convert hex strings to integers 444 | start_range = int(start_range, 16) 445 | end_range = int(end_range, 16) 446 | 447 | # Split range among instances 448 | ranges = self.split_range(start_range, end_range, len(self.console_frames)) 449 | 450 | # Start each instance with its portion of the range 451 | for i, console in enumerate(self.console_frames): 452 | instance_start, instance_end = ranges[i] 453 | self.start_instance(console, instance_start, instance_end, i + 1) 454 | 455 | except ValueError as e: 456 | messagebox.showwarning("Error", f"Invalid range format: {str(e)}\nPlease use hex format (e.g., 400000000000000000:7FFFFFFFFFFFFFFFFF)") 457 | 458 | def start_instance(self, console, start_range, end_range, instance_number): 459 | """Start a single instance with the given range""" 460 | console.append_output(f"Instance {instance_number}/{len(self.console_frames)}") 461 | console.append_output(f"Range: {format(start_range, 'x')} to {format(end_range, 'x')}") 462 | 463 | command = self.construct_command_key(start_range, end_range) 464 | self.execute_command(console, command, instance_number) 465 | 466 | def execute_command(self, console, command, instance_number): 467 | """Execute command and show output in the given console window""" 468 | # Stop existing thread for this instance if it exists 469 | if instance_number in self.command_threads: 470 | self.command_threads[instance_number].terminate() 471 | self.command_threads[instance_number].join() 472 | 473 | # Convert command list to string for display 474 | command_str = ' '.join(str(x) for x in command) 475 | console.append_output(f"Executing command: {command_str}") 476 | 477 | # Create and start new thread for this instance 478 | thread = CommandThread(command, console) 479 | thread.start() 480 | 481 | # Store the thread 482 | self.command_threads[instance_number] = thread 483 | 484 | def stop_all_instances(self): 485 | """Stop all running instances""" 486 | try: 487 | for instance_number, thread in list(self.command_threads.items()): 488 | if thread and thread.is_alive(): 489 | thread.terminate() 490 | thread.join() 491 | 492 | # Kill any remaining keyhunt processes 493 | if platform.system() == "Windows": 494 | try: 495 | subprocess.run(["taskkill", "/f", "/im", "keyhunt.exe"], 496 | stdout=subprocess.DEVNULL, 497 | stderr=subprocess.DEVNULL, 498 | timeout=2) 499 | except subprocess.TimeoutExpired: 500 | pass 501 | except Exception: 502 | pass 503 | else: 504 | try: 505 | subprocess.run(["pkill", "-f", "keyhunt"], 506 | stdout=subprocess.DEVNULL, 507 | stderr=subprocess.DEVNULL, 508 | timeout=2) 509 | except subprocess.TimeoutExpired: 510 | pass 511 | except Exception: 512 | pass 513 | 514 | if instance_number <= len(self.console_frames): 515 | self.console_frames[instance_number - 1].append_output("Process stopped by user") 516 | 517 | # Clear the thread dictionary 518 | self.command_threads.clear() 519 | except Exception as e: 520 | print(f"Error during cleanup: {str(e)}") 521 | 522 | def split_range(self, start, end, num_splits): 523 | """Split a range into equal parts""" 524 | total_range = end - start 525 | chunk_size = total_range // num_splits 526 | remainder = total_range % num_splits 527 | 528 | ranges = [] 529 | current_start = start 530 | 531 | for i in range(num_splits): 532 | extra = 1 if i < remainder else 0 533 | current_end = current_start + chunk_size + extra - 1 534 | if i == num_splits - 1: 535 | current_end = end 536 | ranges.append((current_start, current_end)) 537 | current_start = current_end + 1 538 | 539 | return ranges 540 | 541 | def browse_input_file(self): 542 | file_path = filedialog.askopenfilename( 543 | title="Select Input File", 544 | filetypes=[ 545 | ("Text Files", "*.txt"), 546 | ("Binary Files", "*.bin"), 547 | ("All Files", "*.*") 548 | ] 549 | ) 550 | if file_path: 551 | file_name = os.path.basename(file_path) 552 | self.input_file_entry.delete(0, tk.END) 553 | self.input_file_entry.insert(0, file_name) 554 | 555 | def found_prog(self): 556 | file_path = 'KEYFOUNDKEYFOUND.txt' 557 | self.read_and_display_file(file_path, "😀😀 Keyhunt File found. Check for Winners 😀😀.", "😞😞No Winners Yet 😞😞") 558 | 559 | def read_and_display_file(self, file_path, success_message, error_message): 560 | for console in self.console_frames: 561 | console.append_output(f"Attempting to read file: {file_path}") 562 | try: 563 | if not os.path.exists(file_path): 564 | for console in self.console_frames: 565 | console.append_output(f"⚠️ {error_message} File not found. Please check the file path.") 566 | return None 567 | 568 | with open(file_path, 'r') as file: 569 | output_from_text = file.read() 570 | for console in self.console_frames: 571 | console.append_output(success_message) 572 | console.append_output(output_from_text) 573 | return output_from_text 574 | except FileNotFoundError: 575 | for console in self.console_frames: 576 | console.append_output(f"⚠️ {error_message} File not found. Please check the file path.") 577 | return None 578 | except Exception as e: 579 | for console in self.console_frames: 580 | console.append_output(f"An error occurred: {str(e)}") 581 | return None 582 | 583 | def check_prog(self): 584 | directory = '.' 585 | dat_files = glob.glob(os.path.join(directory, '*.dat')) 586 | 587 | if dat_files: 588 | file_path = dat_files[0] 589 | if messagebox.askyesno("Progress File", "Do you want to delete the progress file?"): 590 | os.remove(file_path) 591 | for console in self.console_frames: 592 | console.append_output("Progress deleted successfully.") 593 | else: 594 | for console in self.console_frames: 595 | console.append_output("Progress kept.") 596 | else: 597 | for console in self.console_frames: 598 | console.append_output("Progress not found.") 599 | 600 | def range_check(self): 601 | # Create a new top-level window 602 | range_window = tk.Toplevel(self.root) 603 | range_window.title("Range Tools") 604 | range_window.geometry("600x400") 605 | 606 | # Create main frame 607 | main_frame = ttk.Frame(range_window, padding="10") 608 | main_frame.pack(fill=tk.BOTH, expand=True) 609 | 610 | # Range Input Section 611 | input_frame = ttk.LabelFrame(main_frame, text="Range Input", padding="5") 612 | input_frame.pack(fill=tk.X, pady=5) 613 | 614 | # Start Range 615 | start_frame = ttk.Frame(input_frame) 616 | start_frame.pack(fill=tk.X, pady=2) 617 | ttk.Label(start_frame, text="Start Range:").pack(side=tk.LEFT, padx=5) 618 | start_entry = ttk.Entry(start_frame, width=50) 619 | start_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) 620 | 621 | # End Range 622 | end_frame = ttk.Frame(input_frame) 623 | end_frame.pack(fill=tk.X, pady=2) 624 | ttk.Label(end_frame, text="End Range:").pack(side=tk.LEFT, padx=5) 625 | end_entry = ttk.Entry(end_frame, width=50) 626 | end_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True) 627 | 628 | # Buttons Frame 629 | button_frame = ttk.Frame(main_frame) 630 | button_frame.pack(fill=tk.X, pady=5) 631 | 632 | def check_range(): 633 | try: 634 | start = int(start_entry.get(), 16) 635 | end = int(end_entry.get(), 16) 636 | if start >= end: 637 | messagebox.showerror("Error", "Start range must be less than end range") 638 | return 639 | 640 | # Calculate range size 641 | range_size = end - start + 1 # Add 1 to include both start and end 642 | bits = (range_size).bit_length() 643 | 644 | # Update result text 645 | result_text.delete(1.0, tk.END) 646 | result_text.insert(tk.END, f"Range Size: {range_size:,} keys\n") 647 | result_text.insert(tk.END, f"Bits: {bits}\n") 648 | result_text.insert(tk.END, f"Start: {hex(start)}\n") 649 | result_text.insert(tk.END, f"End: {hex(end)}\n") 650 | 651 | # Calculate estimated time (assuming 1 million keys per second) 652 | keys_per_second = 1_000_000 653 | estimated_seconds = range_size / keys_per_second 654 | days = int(estimated_seconds // (24 * 3600)) 655 | hours = int((estimated_seconds % (24 * 3600)) // 3600) 656 | minutes = int((estimated_seconds % 3600) // 60) 657 | 658 | result_text.insert(tk.END, f"\nEstimated Time (at 1M keys/sec):\n") 659 | result_text.insert(tk.END, f"{days:,} days, {hours} hours, {minutes} minutes\n") 660 | 661 | except ValueError as e: 662 | messagebox.showerror("Error", "Invalid range format. Please use hexadecimal values.") 663 | 664 | def split_range(): 665 | try: 666 | start = int(start_entry.get(), 16) 667 | end = int(end_entry.get(), 16) 668 | if start >= end: 669 | messagebox.showerror("Error", "Start range must be less than end range") 670 | return 671 | 672 | # Get number of splits 673 | num_splits = simpledialog.askinteger("Split Range", "Enter number of splits:", 674 | minvalue=2, maxvalue=100) 675 | if not num_splits: 676 | return 677 | 678 | # Calculate splits 679 | total_range = end - start 680 | chunk_size = total_range // num_splits 681 | remainder = total_range % num_splits 682 | 683 | # Update result text 684 | result_text.delete(1.0, tk.END) 685 | result_text.insert(tk.END, f"Range split into {num_splits} parts:\n\n") 686 | 687 | current_start = start 688 | for i in range(num_splits): 689 | extra = 1 if i < remainder else 0 690 | current_end = current_start + chunk_size + extra - 1 691 | if i == num_splits - 1: 692 | current_end = end 693 | 694 | result_text.insert(tk.END, f"Part {i+1}:\n") 695 | result_text.insert(tk.END, f"Start: {hex(current_start)}\n") 696 | result_text.insert(tk.END, f"End: {hex(current_end)}\n") 697 | result_text.insert(tk.END, f"Size: {current_end - current_start + 1:,} keys\n\n") 698 | 699 | current_start = current_end + 1 700 | 701 | except ValueError as e: 702 | messagebox.showerror("Error", "Invalid range format. Please use hexadecimal values.") 703 | 704 | def load_current_range(): 705 | try: 706 | current_range = self.keyspace_entry.get().strip() 707 | if current_range: 708 | start, end = current_range.split(':') 709 | start_entry.delete(0, tk.END) 710 | start_entry.insert(0, start) 711 | end_entry.delete(0, tk.END) 712 | end_entry.insert(0, end) 713 | except Exception as e: 714 | messagebox.showerror("Error", "Failed to load current range") 715 | 716 | # Add buttons 717 | ttk.Button(button_frame, text="Check Range", command=check_range).pack(side=tk.LEFT, padx=5) 718 | ttk.Button(button_frame, text="Split Range", command=split_range).pack(side=tk.LEFT, padx=5) 719 | ttk.Button(button_frame, text="Load Current Range", command=load_current_range).pack(side=tk.LEFT, padx=5) 720 | 721 | # Result Text 722 | result_frame = ttk.LabelFrame(main_frame, text="Results", padding="5") 723 | result_frame.pack(fill=tk.BOTH, expand=True, pady=5) 724 | 725 | result_text = tk.Text(result_frame, wrap=tk.WORD, height=10, bg='black', fg='white') 726 | scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=result_text.yview) 727 | result_text.configure(yscrollcommand=scrollbar.set) 728 | 729 | result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) 730 | scrollbar.pack(side=tk.RIGHT, fill=tk.Y) 731 | 732 | # Load current range if available 733 | load_current_range() 734 | 735 | def open_settings(self): 736 | # Create a new top-level window 737 | settings_window = tk.Toplevel(self.root) 738 | settings_window.title("Settings") 739 | settings_window.geometry("500x500") 740 | 741 | # Create main frame 742 | main_frame = ttk.Frame(settings_window, padding="10") 743 | main_frame.pack(fill=tk.BOTH, expand=True) 744 | 745 | # Theme Section 746 | theme_frame = ttk.LabelFrame(main_frame, text="Theme Settings", padding="5") 747 | theme_frame.pack(fill=tk.X, pady=5) 748 | 749 | # Theme Selection 750 | theme_select_frame = ttk.Frame(theme_frame) 751 | theme_select_frame.pack(fill=tk.X, pady=5) 752 | 753 | ttk.Label(theme_select_frame, text="Select Theme:").pack(side=tk.LEFT, padx=5) 754 | self.theme_combo = ttk.Combobox(theme_select_frame, width=20) 755 | self.theme_combo['values'] = [ 756 | "Default Dark", 757 | "Default Light", 758 | "Blue Dark", 759 | "Blue Light", 760 | "Green Dark", 761 | "Green Light", 762 | "Purple Dark", 763 | "Purple Light", 764 | "Solarized Dark", 765 | "Solarized Light", 766 | "Monokai", 767 | "Nord", 768 | "Custom" 769 | ] 770 | self.theme_combo.set("Default Dark") 771 | self.theme_combo.pack(side=tk.LEFT, padx=5) 772 | self.theme_combo.bind('<>', self.preview_theme) 773 | 774 | # Custom Theme Colors 775 | custom_frame = ttk.LabelFrame(theme_frame, text="Custom Theme Colors", padding="5") 776 | custom_frame.pack(fill=tk.X, pady=5) 777 | 778 | # Background Color 779 | bg_frame = ttk.Frame(custom_frame) 780 | bg_frame.pack(fill=tk.X, pady=2) 781 | ttk.Label(bg_frame, text="Background:").pack(side=tk.LEFT, padx=5) 782 | self.bg_color = ttk.Entry(bg_frame, width=10) 783 | self.bg_color.insert(0, "#000000") 784 | self.bg_color.pack(side=tk.LEFT, padx=5) 785 | ttk.Button(bg_frame, text="Pick", command=lambda: self.pick_color("bg")).pack(side=tk.LEFT, padx=5) 786 | 787 | # Text Color 788 | text_frame = ttk.Frame(custom_frame) 789 | text_frame.pack(fill=tk.X, pady=2) 790 | ttk.Label(text_frame, text="Text:").pack(side=tk.LEFT, padx=5) 791 | self.text_color = ttk.Entry(text_frame, width=10) 792 | self.text_color.insert(0, "#FFFFFF") 793 | self.text_color.pack(side=tk.LEFT, padx=5) 794 | ttk.Button(text_frame, text="Pick", command=lambda: self.pick_color("text")).pack(side=tk.LEFT, padx=5) 795 | 796 | # Button Color 797 | button_frame = ttk.Frame(custom_frame) 798 | button_frame.pack(fill=tk.X, pady=2) 799 | ttk.Label(button_frame, text="Button:").pack(side=tk.LEFT, padx=5) 800 | self.button_color = ttk.Entry(button_frame, width=10) 801 | self.button_color.insert(0, "#404040") 802 | self.button_color.pack(side=tk.LEFT, padx=5) 803 | ttk.Button(button_frame, text="Pick", command=lambda: self.pick_color("button")).pack(side=tk.LEFT, padx=5) 804 | 805 | # Preview Section 806 | preview_frame = ttk.LabelFrame(main_frame, text="Theme Preview", padding="5") 807 | preview_frame.pack(fill=tk.BOTH, expand=True, pady=5) 808 | 809 | self.preview_text = tk.Text(preview_frame, wrap=tk.WORD, height=5, bg='black', fg='white') 810 | self.preview_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) 811 | self.preview_text.insert(tk.END, "This is a preview of how the theme will look.\n") 812 | self.preview_text.insert(tk.END, "The console output will use these colors.") 813 | 814 | # Buttons 815 | button_frame = ttk.Frame(main_frame) 816 | button_frame.pack(fill=tk.X, pady=5) 817 | 818 | ttk.Button(button_frame, text="Apply Theme", command=self.apply_theme).pack(side=tk.LEFT, padx=5) 819 | ttk.Button(button_frame, text="Save Theme", command=self.save_theme).pack(side=tk.LEFT, padx=5) 820 | ttk.Button(button_frame, text="Reset to Default", command=self.reset_theme).pack(side=tk.LEFT, padx=5) 821 | 822 | # Load current theme 823 | self.load_current_theme() 824 | 825 | def pick_color(self, color_type): 826 | color = colorchooser.askcolor(title="Choose Color")[1] 827 | if color: 828 | if color_type == "bg": 829 | self.bg_color.delete(0, tk.END) 830 | self.bg_color.insert(0, color) 831 | elif color_type == "text": 832 | self.text_color.delete(0, tk.END) 833 | self.text_color.insert(0, color) 834 | elif color_type == "button": 835 | self.button_color.delete(0, tk.END) 836 | self.button_color.insert(0, color) 837 | self.preview_theme() 838 | 839 | def preview_theme(self, event=None): 840 | theme = self.theme_combo.get() 841 | if theme == "Custom": 842 | bg = self.bg_color.get() 843 | text = self.text_color.get() 844 | button = self.button_color.get() 845 | else: 846 | colors = self.get_theme_colors(theme) 847 | bg = colors['bg'] 848 | text = colors['text'] 849 | button = colors['button'] 850 | 851 | self.preview_text.configure(bg=bg, fg=text) 852 | self.style.configure('TButton', background=button) 853 | self.style.configure('TLabel', background=bg, foreground=text) 854 | self.style.configure('TFrame', background=bg) 855 | self.style.configure('TLabelframe', background=bg) 856 | self.style.configure('TLabelframe.Label', background=bg, foreground=text) 857 | 858 | def get_theme_colors(self, theme): 859 | themes = { 860 | "Default Dark": { 861 | 'bg': '#1E1E1E', # Dark gray 862 | 'text': '#E0E0E0', # Light gray 863 | 'button': '#2D2D2D' # Slightly lighter gray 864 | }, 865 | "Default Light": { 866 | 'bg': '#F5F5F5', # Light gray 867 | 'text': '#2D2D2D', # Dark gray 868 | 'button': '#E0E0E0' # Medium gray 869 | }, 870 | "Blue Dark": { 871 | 'bg': '#0D1117', # GitHub dark blue 872 | 'text': '#58A6FF', # Bright blue 873 | 'button': '#1F6FEB' # Medium blue 874 | }, 875 | "Blue Light": { 876 | 'bg': '#F0F8FF', # Alice blue 877 | 'text': '#0066CC', # Deep blue 878 | 'button': '#B0E0E6' # Powder blue 879 | }, 880 | "Green Dark": { 881 | 'bg': '#0A1929', # Dark navy 882 | 'text': '#00FF9D', # Bright mint 883 | 'button': '#00CC7E' # Medium mint 884 | }, 885 | "Green Light": { 886 | 'bg': '#F0FFF0', # Honeydew 887 | 'text': '#228B22', # Forest green 888 | 'button': '#98FB98' # Pale green 889 | }, 890 | "Purple Dark": { 891 | 'bg': '#1A1B26', # Dark navy 892 | 'text': '#BB9AF7', # Light purple 893 | 'button': '#7AA2F7' # Medium purple 894 | }, 895 | "Purple Light": { 896 | 'bg': '#F8F0FF', # Light lavender 897 | 'text': '#663399', # Rebecca purple 898 | 'button': '#E6E6FA' # Lavender 899 | }, 900 | "Solarized Dark": { 901 | 'bg': '#002B36', # Dark teal 902 | 'text': '#93A1A1', # Light gray 903 | 'button': '#073642' # Darker teal 904 | }, 905 | "Solarized Light": { 906 | 'bg': '#FDF6E3', # Light cream 907 | 'text': '#657B83', # Dark gray 908 | 'button': '#EEE8D5' # Light cream 909 | }, 910 | "Monokai": { 911 | 'bg': '#272822', # Dark gray 912 | 'text': '#F8F8F2', # Off-white 913 | 'button': '#3E3D32' # Medium gray 914 | }, 915 | "Nord": { 916 | 'bg': '#2E3440', # Dark blue-gray 917 | 'text': '#ECEFF4', # Light gray 918 | 'button': '#3B4252' # Medium blue-gray 919 | } 920 | } 921 | return themes.get(theme, themes["Default Dark"]) 922 | 923 | def apply_theme(self): 924 | theme = self.theme_combo.get() 925 | if theme == "Custom": 926 | colors = { 927 | 'bg': self.bg_color.get(), 928 | 'text': self.text_color.get(), 929 | 'button': self.button_color.get() 930 | } 931 | else: 932 | colors = self.get_theme_colors(theme) 933 | 934 | # Apply theme to all console windows 935 | for console in self.console_frames: 936 | console.text.configure(bg=colors['bg'], fg=colors['text']) 937 | 938 | # Save theme to config 939 | self.save_theme_to_config(colors) 940 | 941 | messagebox.showinfo("Theme Applied", "Theme has been applied successfully!") 942 | 943 | def save_theme(self): 944 | theme = self.theme_combo.get() 945 | if theme == "Custom": 946 | colors = { 947 | 'bg': self.bg_color.get(), 948 | 'text': self.text_color.get(), 949 | 'button': self.button_color.get() 950 | } 951 | # Save custom theme 952 | self.save_theme_to_config(colors) 953 | messagebox.showinfo("Theme Saved", "Custom theme has been saved!") 954 | else: 955 | messagebox.showinfo("Theme Saved", f"{theme} theme is already saved!") 956 | 957 | def save_theme_to_config(self, colors): 958 | config = configparser.ConfigParser() 959 | if os.path.exists('config.ini'): 960 | config.read('config.ini') 961 | 962 | if not config.has_section('Theme'): 963 | config.add_section('Theme') 964 | 965 | config['Theme']['background'] = colors['bg'] 966 | config['Theme']['text'] = colors['text'] 967 | config['Theme']['button'] = colors['button'] 968 | 969 | with open('config.ini', 'w') as f: 970 | config.write(f) 971 | 972 | def load_current_theme(self): 973 | config = configparser.ConfigParser() 974 | if os.path.exists('config.ini'): 975 | config.read('config.ini') 976 | if config.has_section('Theme'): 977 | self.bg_color.delete(0, tk.END) 978 | self.bg_color.insert(0, config.get('Theme', 'background', fallback='#000000')) 979 | self.text_color.delete(0, tk.END) 980 | self.text_color.insert(0, config.get('Theme', 'text', fallback='#FFFFFF')) 981 | self.button_color.delete(0, tk.END) 982 | self.button_color.insert(0, config.get('Theme', 'button', fallback='#404040')) 983 | self.theme_combo.set("Custom") 984 | self.preview_theme() 985 | 986 | def reset_theme(self): 987 | self.theme_combo.set("Default Dark") 988 | self.preview_theme() 989 | self.apply_theme() 990 | 991 | def open_telegram(self): 992 | webbrowser.open("https://t.me/TeamHunter_GUI") 993 | 994 | def about(self): 995 | about_text = """ 996 | KeyHunter Puzzles GUI - Advanced Bitcoin Key Hunting Tool made with Tkinter the Tk GUI toolkit for Python 997 | 998 | Major Features: 999 | 1. Multi-Instance Support 1000 | 2. Theme Customization 1001 | 3. Advanced Range Management 1002 | 4. Enhanced Scanning Capabilities 1003 | 5. User-Friendly Interface 1004 | 1005 | Made by Team Mizogg 1006 | © mizogg.com 2018 - 2025 1007 | """ 1008 | messagebox.showinfo("About", about_text) 1009 | 1010 | def add_credits(self): 1011 | credits_frame = ttk.Frame(self.main_container) 1012 | credits_frame.pack(fill=tk.X, pady=5) 1013 | 1014 | credits_text = "Tkinter GUI • Made by Team Mizogg • Full Version 1.2 • © mizogg.com 2018 - 2025" 1015 | ttk.Label(credits_frame, text=credits_text).pack() 1016 | 1017 | def load_config(self): 1018 | config = configparser.ConfigParser() 1019 | if os.path.exists('config.ini'): 1020 | config.read('config.ini') 1021 | # TODO: Load theme settings 1022 | 1023 | if __name__ == "__main__": 1024 | root = tk.Tk() 1025 | app = KeyHunterGUI(root) 1026 | root.mainloop() 1027 | --------------------------------------------------------------------------------