├── 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 | 
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 | 
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 |
--------------------------------------------------------------------------------