import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext, messagebox
import openpyxl
from pathlib import Path
import threading
from datetime import datetime
import re
import warnings
# Suppress escape sequence warnings
warnings.filterwarnings("ignore", category=SyntaxWarning)
class BlackhatExcelSearcher(tk.Tk):
def __init__(self):
super().__init__()
self.title("🔥 EXCEL SHADOW SEARCH v3.3 - FULL ROW MODE 🔥")
self.geometry("1250x820")
self.configure(bg='#0a0a0a')
self.folder_paths = []
self.search_text = tk.StringVar()
self.is_searching = False
self.create_widgets()
def create_widgets(self):
main_frame = ttk.Frame(self, padding="10")
main_frame.grid(row=0, column=0, sticky="nsew")
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
ttk.Label(main_frame, text="EXCEL SHADOW SEARCH v3.3 - FULL ROW",
font=('Consolas', 16, 'bold'), foreground='#00ff00').grid(row=0, column=0, columnspan=4, pady=10)
# Folders
folder_frame = ttk.LabelFrame(main_frame, text="TARGET FOLDERS", padding=8)
folder_frame.grid(row=1, column=0, columnspan=4, sticky="ew", pady=5)
self.folder_listbox = tk.Listbox(folder_frame, height=6, bg='#1e1e1e', fg='#00ff41')
self.folder_listbox.pack(fill=tk.X, padx=5, pady=5)
btnf = ttk.Frame(folder_frame)
btnf.pack(fill=tk.X)
ttk.Button(btnf, text="Add Folder", command=self.add_folder).pack(side=tk.LEFT, padx=5)
ttk.Button(btnf, text="Remove", command=self.remove_folder).pack(side=tk.LEFT, padx=5)
ttk.Button(btnf, text="Clear", command=self.clear_folders).pack(side=tk.LEFT)
# Search
sf = ttk.LabelFrame(main_frame, text="HUNT CONFIG", padding=10)
sf.grid(row=2, column=0, columnspan=4, sticky="ew", pady=8)
ttk.Label(sf, text="Search Term:").grid(row=0, column=0, sticky=tk.W, padx=5)
ttk.Entry(sf, textvariable=self.search_text, width=55, font=('Consolas', 11)).grid(row=0, column=1, padx=5, sticky="ew")
self.search_btn = ttk.Button(sf, text="🚀 START SEARCH", command=self.start_search)
self.search_btn.grid(row=0, column=2, padx=10)
self.fuzzy_var = tk.BooleanVar(value=True)
ttk.Checkbutton(sf, text="Enable Number Cleaning + Fuzzy", variable=self.fuzzy_var).grid(row=1, column=0, columnspan=3, pady=5)
self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
self.progress.grid(row=3, column=0, columnspan=4, sticky="ew", pady=5)
# Results
rf = ttk.LabelFrame(main_frame, text="RESULTS (Full Row Shown on Match)", padding=8)
rf.grid(row=4, column=0, columnspan=4, sticky="nsew")
rf.rowconfigure(0, weight=1)
rf.columnconfigure(0, weight=1)
self.results_text = scrolledtext.ScrolledText(rf, bg='#0a0a0a', fg='#00ff41', font=('Consolas', 10))
self.results_text.grid(row=0, column=0, sticky="nsew")
ttk.Button(main_frame, text="💾 Save Results", command=self.save_results).grid(row=5, column=0, columnspan=4, pady=8)
self.status = ttk.Label(main_frame, text="Ready", relief=tk.SUNKEN, foreground="#00ff00")
self.status.grid(row=6, column=0, columnspan=4, sticky="ew")
def add_folder(self):
folder = filedialog.askdirectory()
if folder and folder not in self.folder_paths:
self.folder_paths.append(folder)
self.folder_listbox.insert(tk.END, folder)
def remove_folder(self):
sel = self.folder_listbox.curselection()
if sel:
idx = sel[0]
self.folder_listbox.delete(idx)
del self.folder_paths[idx]
def clear_folders(self):
self.folder_paths.clear()
self.folder_listbox.delete(0, tk.END)
def log(self, message):
self.results_text.insert(tk.END, f"[LOG] {message}\n")
self.results_text.see(tk.END)
def get_full_row(self, sheet, row_idx):
"""Get all values in a row as tab-separated string"""
row_values = []
for cell in sheet[row_idx]:
val = cell.value
row_values.append(str(val).strip() if val is not None else "")
return " | ".join(row_values)
def search_excel_file(self, file_path, term):
matches = []
try:
self.log(f"Scanning {file_path.name}")
wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True)
for sheet_name in wb.sheetnames:
sheet = wb[sheet_name]
self.log(f" → Sheet: {sheet_name} ({sheet.max_row} rows)")
for r_idx, row in enumerate(sheet.iter_rows(values_only=True), start=1):
for c_idx, value in enumerate(row, start=1):
if value is None:
continue
cell_str = str(value).strip()
if not cell_str:
continue
found = False
col_letter = openpyxl.utils.get_column_letter(c_idx)
# Direct match
if term.lower() in cell_str.lower():
found = True
# Number cleaning
if not found and self.fuzzy_var.get():
clean_cell = re.sub(r'[^0-9a-zA-Z]', '', cell_str.lower())
clean_term = re.sub(r'[^0-9a-zA-Z]', '', term.lower())
if clean_term and clean_term in clean_cell:
found = True
if found:
full_row = self.get_full_row(sheet, r_idx)
matches.append({
'file': str(file_path),
'sheet': sheet_name,
'location': f"{sheet_name}!{col_letter}{r_idx}",
'matched_value': cell_str,
'full_row': full_row
})
break # One match per row is enough (to avoid duplicates)
wb.close()
if matches:
self.log(f" → FOUND {len(matches)} matching rows in {file_path.name}")
except Exception as e:
self.log(f"ERROR {file_path.name}: {e}")
return matches
def start_search(self):
if not self.folder_paths:
messagebox.showwarning("Error", "Add at least one folder!")
return
term = self.search_text.get().strip()
if not term:
messagebox.showwarning("Error", "Enter search term!")
return
self.is_searching = True
self.search_btn.config(state='disabled')
self.progress.start(10)
self.results_text.delete(1.0, tk.END)
self.log(f"Starting hunt for: '{term}'")
thread = threading.Thread(target=self.perform_search, args=(term,))
thread.daemon = True
thread.start()
def perform_search(self, term):
all_results = []
total_files = 0
for folder in self.folder_paths:
p = Path(folder)
excel_files = list(p.glob("**/*.xlsx")) + list(p.glob("**/*.xlsm")) + list(p.glob("**/*.xls"))
total_files += len(excel_files)
for file_path in excel_files:
if file_path.name.startswith('\~$'):
continue
file_matches = self.search_excel_file(file_path, term)
if file_matches:
all_results.extend(file_matches)
self.after(0, lambda: self.show_results(all_results, total_files, term))
def show_results(self, all_matches, total_files, term):
self.progress.stop()
self.search_btn.config(state='normal')
self.is_searching = False
header = f"\n{'='*100}\nFULL ROW SEARCH REPORT - {datetime.now().strftime('%Y-%m-%d %H:%M')}\n"
header += f"Search Term: {term} | Files Scanned: {total_files} | Matching Rows: {len(all_matches)}\n{'='*100}\n\n"
self.results_text.insert(tk.END, header)
if not all_matches:
self.results_text.insert(tk.END, "No matches found.\n")
return
for m in all_matches:
self.results_text.insert(tk.END, f"📁 {m['file']}\n")
self.results_text.insert(tk.END, f"📍 Location : {m['location']}\n")
self.results_text.insert(tk.END, f"🔍 Matched : {m['matched_value']}\n")
self.results_text.insert(tk.END, f"📋 Full Row: {m['full_row']}\n")
self.results_text.insert(tk.END, "-"*120 + "\n\n")
self.status.config(text=f"Complete → {len(all_matches)} matching rows found")
def save_results(self):
content = self.results_text.get(1.0, tk.END).strip()
if not content:
return
path = filedialog.asksaveasfilename(defaultextension=".txt",
initialfile=f"search_results_{datetime.now().strftime('%Y%m%d_%H%M')}.txt")
if path:
with open(path, "w", encoding="utf-8") as f:
f.write(content)
messagebox.showinfo("Saved", f"Results saved to {path}")
if __name__ == "__main__":
app = BlackhatExcelSearcher()
app.mainloop()13 views