OrbitMC commited on
Commit
ac50132
Β·
verified Β·
1 Parent(s): 3a6b8ac

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -0
app.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import time
4
+ import threading
5
+ import json
6
+ import os
7
+ import itertools
8
+ import string
9
+
10
+ # --- CONFIGURATION ---
11
+ CHARS = string.ascii_lowercase + string.digits + "_"
12
+ DELAY_SECONDS = 2.0 # Safe delay to prevent IP bans from APIs
13
+ STATE_FILE = "hunter_state.json"
14
+
15
+ # Generate all 50,653 combinations deterministically
16
+ ALL_COMBOS = ["".join(c) for c in itertools.product(CHARS, repeat=3)]
17
+ TOTAL_COMBOS = len(ALL_COMBOS)
18
+
19
+ # --- STATE MANAGEMENT ---
20
+ def load_state():
21
+ if os.path.exists(STATE_FILE):
22
+ try:
23
+ with open(STATE_FILE, "r") as f:
24
+ return json.load(f)
25
+ except:
26
+ pass
27
+ return {"index": 0, "available_names": [], "is_running": True}
28
+
29
+ def save_state(state):
30
+ with open(STATE_FILE, "w") as f:
31
+ json.dump(state, f)
32
+
33
+ # Global state loaded into memory
34
+ app_state = load_state()
35
+
36
+ # --- BACKGROUND WORKER ---
37
+ def background_scanner():
38
+ while app_state["index"] < TOTAL_COMBOS:
39
+ if not app_state.get("is_running", True):
40
+ time.sleep(5)
41
+ continue
42
+
43
+ current_name = ALL_COMBOS[app_state["index"]]
44
+
45
+ try:
46
+ # Using PlayerDB to avoid direct Mojang IP rate-limits
47
+ response = requests.get(
48
+ f"https://playerdb.co/api/player/minecraft/{current_name}",
49
+ timeout=10
50
+ )
51
+
52
+ # 404 or success=false means the player does not exist (Name is available)
53
+ if response.status_code == 404 or (response.status_code == 200 and not response.json().get("success")):
54
+ if current_name not in app_state["available_names"]:
55
+ app_state["available_names"].append(current_name)
56
+ print(f"[FOUND] {current_name} is available!")
57
+
58
+ # If we hit a rate limit, back off for a minute
59
+ elif response.status_code == 429:
60
+ print("[RATE LIMIT] Sleeping for 60 seconds...")
61
+ time.sleep(60)
62
+ continue # Retry same index
63
+
64
+ except Exception as e:
65
+ print(f"[NETWORK ERROR] {e}. Retrying in 10s...")
66
+ time.sleep(10)
67
+ continue # Retry same index
68
+
69
+ # Move forward and save
70
+ app_state["index"] += 1
71
+
72
+ # Save to disk every 10 checks to minimize disk I/O but ensure persistence
73
+ if app_state["index"] % 10 == 0:
74
+ save_state(app_state)
75
+
76
+ time.sleep(DELAY_SECONDS)
77
+
78
+ # Final save when complete
79
+ app_state["is_running"] = False
80
+ save_state(app_state)
81
+
82
+ # Start the background thread (daemon=True means it dies when the app dies)
83
+ scanner_thread = threading.Thread(target=background_scanner, daemon=True)
84
+ scanner_thread.start()
85
+
86
+ # --- UI FUNCTIONS ---
87
+ def get_dashboard_stats():
88
+ idx = app_state["index"]
89
+ percent = (idx / TOTAL_COMBOS) * 100
90
+ names = app_state["available_names"]
91
+
92
+ status_msg = "🟒 Running in Background" if idx < TOTAL_COMBOS else "βœ… Scan Complete"
93
+
94
+ stats_markdown = f"""
95
+ ### πŸ“Š Scanner Status: {status_msg}
96
+ * **Progress:** {idx:,} / {TOTAL_COMBOS:,} ({percent:.2f}%)
97
+ * **Available Names Found:** {len(names)}
98
+ * **Currently Checking:** `{ALL_COMBOS[min(idx, TOTAL_COMBOS-1)]}`
99
+ """
100
+
101
+ names_list = "\n".join(names) if names else "No names found yet. Check back later!"
102
+ return stats_markdown, names_list
103
+
104
+ # --- GRADIO UI ---
105
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo"), title="3-Letter MC Hunter") as demo:
106
+ gr.Markdown(
107
+ """
108
+ # πŸ•΅οΈβ€β™‚οΈ Autonomous 3-Letter Name Hunter
109
+ This tool runs continuously in the background checking all 50,653 combinations of A-Z, 0-9, and _.
110
+ You can safely close this tab; the server will keep scanning.
111
+ """
112
+ )
113
+
114
+ with gr.Row():
115
+ with gr.Column(scale=1):
116
+ stats_display = gr.Markdown(value="Loading stats...")
117
+ refresh_btn = gr.Button("πŸ”„ Refresh Dashboard", variant="primary")
118
+
119
+ with gr.Column(scale=2):
120
+ gr.Markdown("### πŸ’Ž Available Names Vault")
121
+ names_display = gr.TextArea(
122
+ label="Available Names (Copy & Paste)",
123
+ interactive=False,
124
+ lines=15
125
+ )
126
+
127
+ # Wire up the refresh button
128
+ refresh_btn.click(
129
+ fn=get_dashboard_stats,
130
+ inputs=[],
131
+ outputs=[stats_display, names_display]
132
+ )
133
+
134
+ # Run once on load
135
+ demo.load(
136
+ fn=get_dashboard_stats,
137
+ inputs=[],
138
+ outputs=[stats_display, names_display]
139
+ )
140
+
141
+ if __name__ == "__main__":
142
+ demo.launch()