overmind-mcp 2.8.48 → 2.8.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,871 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ gen_evm_key_retro.py — Générateur de clés privées EVM style KEYGEN des années 2000.
4
+ Dimensions verrouillées : 500x360 pixels.
5
+ Thème visuel : Ville Overmind en parallaxe sur 3 couches, route perspective 3D animée, scanlines CRT.
6
+ Thème sonore : Syntheur Chiptune basse fréquence sans aucun aigu strident (style Mephisto Attack).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+ import math
14
+ import os
15
+ import random
16
+ import struct
17
+ import sys
18
+ import threading
19
+ import time
20
+ from typing import Any
21
+
22
+ # =========================================================================
23
+ # SYSTEME DE GENERATION DE CLES CRYPTOGRAPHIQUES EVM (EIP-55 Checksum)
24
+ # =========================================================================
25
+ try:
26
+ # eth_account utilise le CSPRNG du système d'exploitation sous le capot
27
+ from eth_account import Account
28
+ except ImportError:
29
+ sys.stderr.write("Erreur : le module 'eth-account' est manquant.\n")
30
+ sys.stderr.write("Veuillez l'installer avec : pip install eth-account\n")
31
+ sys.exit(1)
32
+
33
+
34
+ def gen_one(no_0x: bool = False) -> dict[str, str]:
35
+ """
36
+ Génère une clé privée de 32 octets via un générateur pseudo-aléatoire cryptographiquement sûr.
37
+ Dérive ensuite l'adresse publique Ethereum avec le checksum conforme à la norme EIP-55.
38
+
39
+ Args:
40
+ no_0x (bool): Si True, la clé privée sera retournée sans le préfixe '0x'.
41
+
42
+ Returns:
43
+ dict[str, str]: Contient la clé privée sous 'private_key' et l'adresse sous 'address'.
44
+ """
45
+ # Création d'un compte éphémère sécurisé
46
+ acct = Account.create()
47
+ priv = acct.key.hex()
48
+ if not no_0x:
49
+ priv = "0x" + priv
50
+ return {"private_key": priv, "address": acct.address}
51
+
52
+
53
+ # Détection de la disponibilité de l'interface graphique Tkinter
54
+ GUI_AVAILABLE = False
55
+ try:
56
+ import tkinter as tk
57
+ from tkinter import font as tkfont
58
+ GUI_AVAILABLE = True
59
+ except ImportError:
60
+ pass
61
+
62
+ # Détection de la compatibilité audio Windows
63
+ WINSOUND_AVAILABLE = False
64
+ if sys.platform == "win32":
65
+ try:
66
+ import winsound
67
+ WINSOUND_AVAILABLE = True
68
+ except ImportError:
69
+ pass
70
+
71
+
72
+ # =========================================================================
73
+ # CHIPTUNE SYNTH — Moteur Audio Basse Fréquence (Anti-Aigu & Anti-Clics)
74
+ # =========================================================================
75
+ class ChiptuneSynth:
76
+ """
77
+ Générateur de musique chiptune rétro 8-bit mono à 22 050 Hz.
78
+ Toutes les notes ont été transposées d'une octave supplémentaire vers le bas (40 Hz - 130 Hz)
79
+ afin d'éliminer toute fréquence aiguë ou stridence agaçante pour l'utilisateur.
80
+ Le signal utilise une enveloppe linéaire d'attaque et de relâchement pour éviter les bruits de clic.
81
+ """
82
+
83
+ SAMPLE_RATE = 22050 # Fréquence d'échantillonnage standard chiptune
84
+
85
+ def __init__(self) -> None:
86
+ self.playing = False
87
+ self.temp_file = None
88
+ self.wav_data: bytes | None = None
89
+ self._generate_wav() # Prégénération du signal en mémoire pour un chargement instantané
90
+
91
+ def _generate_wav(self) -> None:
92
+ """
93
+ Génère les échantillons PCM 16-bit et construit le conteneur binaire au format RIFF/WAVE.
94
+ La structure de la chanson comprend : Couplet x2 -> Refrain x2 -> Couplet x1 -> Pont x1 -> Refrain x2.
95
+
96
+ Nettoie aussi tout fichier temporaire orphelin laissé par une session crashée précédente.
97
+ """
98
+ import io
99
+
100
+ # --- Purge fichier temporaire orphelin d'une session précédente crashée ---
101
+ try:
102
+ import tempfile
103
+ _orphan = os.path.join(tempfile.gettempdir(), "overmind_keygen_music.wav")
104
+ if os.path.exists(_orphan):
105
+ os.remove(_orphan)
106
+ except Exception:
107
+ pass
108
+
109
+ # Fréquences transposées très bas (basse lourde et chaude)
110
+ # 1. Couplet (Riff sombre et rapide en E-Phrygien)
111
+ verse = [
112
+ 41.20, 41.20, 43.65, 41.20, 49.00, 43.65, 41.20, 43.65,
113
+ 41.20, 41.20, 61.74, 55.00, 49.00, 43.65, 41.20, 43.65
114
+ ]
115
+
116
+ # 2. Refrain (Ligne mélodique plus présente et enveloppante)
117
+ chorus = [
118
+ 55.00, 65.41, 82.41, 110.00, 98.00, 82.41, 87.31, 73.42,
119
+ 55.00, 65.41, 82.41, 110.00, 123.47, 130.81, 110.00, 98.00
120
+ ]
121
+
122
+ # 3. Pont (Pulsations lourdes de sub-basse)
123
+ bridge = [
124
+ 32.70, 32.70, 41.20, 32.70, 36.71, 36.71, 43.65, 36.71,
125
+ 49.00, 49.00, 55.00, 49.00, 55.00, 55.00, 65.41, 55.00
126
+ ]
127
+
128
+ # Assemblage structurel de la boucle audio
129
+ melody = verse * 2 + chorus * 2 + verse + bridge + chorus * 2
130
+
131
+ note_duration = 0.09 # Durée de 90ms par note (rythme rapide d'arpégiateur)
132
+ samples_per_note = int(self.SAMPLE_RATE * note_duration)
133
+
134
+ data = []
135
+ for freq in melody:
136
+ for i in range(samples_per_note):
137
+ t = i / self.SAMPLE_RATE
138
+ # Génération de l'onde sinusoïdale pure (plus douce que les ondes carrées)
139
+ val = math.sin(2 * math.pi * freq * t)
140
+
141
+ # Enveloppe ADSR simplifiée (Attaque de 15% et Relâchement de 25%) pour supprimer les clics
142
+ envelope = 1.0
143
+ fade_in_samples = int(samples_per_note * 0.15)
144
+ fade_out_samples = int(samples_per_note * 0.25)
145
+
146
+ if i < fade_in_samples:
147
+ envelope = i / fade_in_samples
148
+ elif i > (samples_per_note - fade_out_samples):
149
+ envelope = (samples_per_note - i) / fade_out_samples
150
+
151
+ # Amplitude calibrée à 6% maximum pour rester une musique d'ambiance très discrète
152
+ val_scaled = int(val * 32767 * 0.06 * envelope)
153
+ data.append(val_scaled)
154
+
155
+ # En-tête standard du format audio WAVE (PCM 16-bit Mono)
156
+ num_samples = len(data)
157
+ data_bytes = struct.pack(f"<{num_samples}h", *data)
158
+ byte_rate = self.SAMPLE_RATE * 2
159
+ data_size = num_samples * 2
160
+
161
+ buffer = io.BytesIO()
162
+ buffer.write(b"RIFF")
163
+ buffer.write(struct.pack("<I", 36 + data_size))
164
+ buffer.write(b"WAVEfmt ")
165
+ buffer.write(struct.pack("<IHHIIHH", 16, 1, 1, self.SAMPLE_RATE, byte_rate, 2, 16))
166
+ buffer.write(b"data")
167
+ buffer.write(struct.pack("<I", data_size))
168
+ buffer.write(data_bytes)
169
+ self.wav_data = buffer.getvalue()
170
+
171
+ def start(self) -> None:
172
+ """Démarre la lecture asynchrone de la musique en boucle depuis un fichier temporaire."""
173
+ if not WINSOUND_AVAILABLE or not self.wav_data:
174
+ return
175
+ self.playing = True
176
+ try:
177
+ import tempfile
178
+ # Écriture du buffer binaire sur le disque pour contourner les limitations SND_MEMORY de Windows
179
+ self.temp_file = os.path.join(tempfile.gettempdir(), "overmind_keygen_music.wav")
180
+ with open(self.temp_file, "wb") as f:
181
+ f.write(self.wav_data)
182
+ # Lecture asynchrone en boucle infinie
183
+ winsound.PlaySound(self.temp_file, winsound.SND_FILENAME | winsound.SND_ASYNC | winsound.SND_LOOP)
184
+ except Exception:
185
+ pass
186
+
187
+ def stop(self) -> None:
188
+ """Arrête la musique et supprime proprement le fichier WAV temporaire."""
189
+ self.playing = False
190
+ if WINSOUND_AVAILABLE:
191
+ try:
192
+ winsound.PlaySound(None, winsound.SND_PURGE)
193
+ if self.temp_file and os.path.exists(self.temp_file):
194
+ os.remove(self.temp_file)
195
+ except Exception:
196
+ pass
197
+
198
+ def toggle(self) -> bool:
199
+ """Bascule entre la lecture et la mise en pause."""
200
+ if self.playing:
201
+ self.stop()
202
+ return False
203
+ self.start()
204
+ return True
205
+
206
+ def reveal_beep(self) -> None:
207
+ """Génère un signal sonore de transition grave/médium doux lors du reveal de la clé."""
208
+ if not WINSOUND_AVAILABLE:
209
+ return
210
+ try:
211
+ # Balayage fréquentiel grave (120 Hz à 240 Hz) pour éviter tout sifflement strident
212
+ sps = int(self.SAMPLE_RATE * 0.18)
213
+ raw = bytearray()
214
+ for i in range(sps):
215
+ t = i / self.SAMPLE_RATE
216
+ f = 120.0 + (240.0 - 120.0) * (i / sps)
217
+ val = math.sin(2 * math.pi * f * t)
218
+ env = 1.0 - (i / sps) * 0.8
219
+ v = int(val * 127 * 0.3 * env)
220
+ raw.append((v + 128) & 0xFF)
221
+
222
+ data_size = len(raw)
223
+ buf = bytearray()
224
+ buf += b"RIFF"
225
+ buf += struct.pack("<I", 36 + data_size)
226
+ buf += b"WAVEfmt "
227
+ buf += struct.pack("<IHHIIHH", 16, 1, 1, self.SAMPLE_RATE, self.SAMPLE_RATE, 1, 8)
228
+ buf += b"data"
229
+ buf += struct.pack("<I", data_size)
230
+ buf += raw
231
+ winsound.PlaySound(bytes(buf), winsound.SND_MEMORY | winsound.SND_ASYNC)
232
+ except Exception:
233
+ pass
234
+
235
+
236
+ # =========================================================================
237
+ # APPLICATION GRAPHIQUE Retro Keygen 2000 (Tkinter)
238
+ # =========================================================================
239
+ class KeygenApp:
240
+ # Palette de couleurs Cyberpunk néon
241
+ BG_DEEP = "#000000"
242
+ BG_PANEL = "#07070d"
243
+ BG_TITLE = "#0f0f15"
244
+ FG_GREEN = "#00ff41"
245
+ FG_CYAN = "#00ffff"
246
+ FG_MAG = "#ff00ff"
247
+ FG_YEL = "#ffff00"
248
+ FG_RED = "#ff0055"
249
+
250
+ # Dimensions fixes obligatoires du Keygen
251
+ W = 500
252
+ H = 360
253
+ CANVAS_H = 216 # Hauteur dédiée à la scène graphique animée
254
+
255
+ def __init__(self, root: tk.Tk) -> None:
256
+ self.root = root
257
+ self.root.title("OVERMIND BEARER KEYGEN 2000")
258
+ self.root.configure(bg=self.BG_DEEP)
259
+ self.root.resizable(False, False)
260
+
261
+ # Mode Borderless (pas de bordures système Windows)
262
+ self.root.overrideredirect(True)
263
+ sw, sh = root.winfo_screenwidth(), root.winfo_screenheight()
264
+ x, y = (sw - self.W) // 2, (sh - self.H) // 2
265
+ self.root.geometry(f"{self.W}x{self.H}+{x}+{y}")
266
+
267
+ # Variables pour permettre le déplacement (drag & drop) de la fenêtre
268
+ self._drag_x: int | None = None
269
+ self._drag_y: int | None = None
270
+ root.bind("<ButtonPress-1>", self._on_press)
271
+ root.bind("<ButtonRelease-1>", self._on_release)
272
+ root.bind("<B1-Motion>", self._on_motion)
273
+
274
+ # Raccourcis clavier (Échap pour quitter, F1 pour générer)
275
+ root.bind("<Escape>", lambda e: self._close())
276
+ root.bind("<F1>", lambda e: self._start_generation())
277
+
278
+ # Variables d'état des animations
279
+ self.grid_offset = 0.0
280
+ self.traffic_offset = 0.0
281
+ self.scan_y = 0
282
+ self.stars: list[dict[str, Any]] = []
283
+
284
+ # ---------------------------------------------------------------------
285
+ # DESIGN DE LA SCENE URBAINE DE LA VILLE EN PARALLAXE (3 COUCHES)
286
+ # ---------------------------------------------------------------------
287
+
288
+ # 1. COUCHE ARRIÈRE-PLAN (Lente, silhouettes très sombres)
289
+ # Défilement : -0.07px par frame
290
+ self.buildings_far = [
291
+ {"x": 10.0, "w": 60, "h": 160, "col": "#0b0417"},
292
+ {"x": 100.0, "w": 75, "h": 140, "col": "#05081f"},
293
+ {"x": 200.0, "w": 55, "h": 170, "col": "#0b0417"},
294
+ {"x": 290.0, "w": 80, "h": 150, "col": "#05081f"},
295
+ {"x": 390.0, "w": 65, "h": 165, "col": "#0b0417"},
296
+ {"x": 480.0, "w": 70, "h": 135, "col": "#05081f"}
297
+ ]
298
+
299
+ # 2. COUCHE INTERMÉDIAIRE (Vitesse moyenne, immeubles filaires mauves)
300
+ # Défilement : -0.18px par frame
301
+ self.buildings_mid = [
302
+ {"x": 30.0, "w": 45, "h": 115, "col": "#1f0933", "roof": 1},
303
+ {"x": 110.0, "w": 55, "h": 135, "col": "#101647", "roof": 0},
304
+ {"x": 210.0, "w": 50, "h": 120, "col": "#1f0933", "roof": 3},
305
+ {"x": 310.0, "w": 60, "h": 145, "col": "#101647", "roof": 1},
306
+ {"x": 410.0, "w": 45, "h": 125, "col": "#1f0933", "roof": 0}
307
+ ]
308
+
309
+ # 3. COUCHE PREMIER PLAN (Rapide, détails néons, antennes, panneaux publicitaires)
310
+ # Défilement : -0.48px par frame
311
+ # types de toit (roof): 0=plat, 1=escalier, 2=antenne + feu de signalisation, 3=dôme, 4=double flèche
312
+ self.buildings_fg = [
313
+ {"x": 5.0, "w": 40, "h": 90, "col": "#b800b8", "roof": 2, "sign": "OVR"},
314
+ {"x": 65.0, "w": 30, "h": 120, "col": "#0055ff", "roof": 0, "sign": ""},
315
+ {"x": 115.0, "w": 35, "h": 75, "col": "#5a00bd", "roof": 1, "sign": ""},
316
+ {"x": 170.0, "w": 45, "h": 135, "col": "#ff00ff", "roof": 4, "sign": "EVM"},
317
+ {"x": 235.0, "w": 30, "h": 85, "col": "#0088ff", "roof": 0, "sign": ""},
318
+ {"x": 285.0, "w": 35, "h": 110, "col": "#00ffcc", "roof": 3, "sign": "SYS"},
319
+ {"x": 340.0, "w": 40, "h": 130, "col": "#990099", "roof": 2, "sign": ""},
320
+ {"x": 400.0, "w": 45, "h": 115, "col": "#00ff99", "roof": 1, "sign": ""},
321
+ {"x": 465.0, "w": 35, "h": 80, "col": "#0055ff", "roof": 0, "sign": ""}
322
+ ]
323
+
324
+ self.current_key: dict[str, str] | None = None
325
+
326
+ # --- Splash LOADING style keygen 2000 (1.5s) ---
327
+ self._splash_start = time.time()
328
+ self._splash_label: tk.Label | None = None
329
+ self._splash_bar: int = 0
330
+
331
+ self._build_ui()
332
+ self._show_splash()
333
+
334
+ # Génération aléatoire d'un champ d'étoiles colorées et scintillantes
335
+ for _ in range(40):
336
+ self.stars.append({
337
+ "x": random.randint(2, self.W - 2),
338
+ "y": random.randint(2, 128),
339
+ "color": random.choice(["#ffffff", "#00ffff", "#ff00ff", "#8888aa"]),
340
+ "size": random.choice([1, 2])
341
+ })
342
+
343
+ # Initialisation et lecture de la musique
344
+ self.synth = ChiptuneSynth()
345
+ self.synth.start()
346
+
347
+ # Démarrage des boucles temporelles d'animation Tkinter
348
+ self._tick_city()
349
+ self._tick_glitch()
350
+
351
+ # Event Handlers pour le déplacement sans bordure de la fenêtre
352
+ def _on_press(self, e):
353
+ self._drag_x = e.x
354
+ self._drag_y = e.y
355
+
356
+ def _on_release(self, e):
357
+ self._drag_x = None
358
+ self._drag_y = None
359
+
360
+ def _on_motion(self, e):
361
+ if self._drag_x is None:
362
+ return
363
+ dx = e.x - self._drag_x
364
+ dy = e.y - self._drag_y
365
+ self.root.geometry(f"+{self.root.winfo_x() + dx}+{self.root.winfo_y() + dy}")
366
+
367
+ def _tick_city(self) -> None:
368
+ """Dessine et anime l'ensemble de la scène graphique néon de façon synchrone."""
369
+ self.canvas_bg.delete("all")
370
+
371
+ horizon_y = 130
372
+ max_y = self.CANVAS_H
373
+
374
+ # 1. ÉTOILES SCINTILLANTES
375
+ for star in self.stars:
376
+ # Twinkle discret par variation d'intensité aléatoire
377
+ color = star["color"]
378
+ if random.random() < 0.08:
379
+ color = random.choice(["#333344", "#555566", star["color"]])
380
+
381
+ s = star["size"]
382
+ self.canvas_bg.create_oval(
383
+ star["x"], star["y"], star["x"] + s, star["y"] + s,
384
+ fill=color, outline=""
385
+ )
386
+
387
+ # 2. SILHOUETTES URBAINES D'ARRIÈRE-PLAN (COUCHE 1 - LENTE)
388
+ for b in self.buildings_far:
389
+ b["x"] -= 0.07
390
+ if b["x"] + b["w"] < 0:
391
+ b["x"] = self.W
392
+
393
+ x, w, h, col = b["x"], b["w"], b["h"], b["col"]
394
+ top_y = horizon_y - h
395
+ # Rectangle plein avec bordure wireframe très sombre
396
+ self.canvas_bg.create_rectangle(
397
+ x, top_y, x + w, horizon_y,
398
+ fill="#020006", outline=col, width=1
399
+ )
400
+
401
+ # 3. IMMEUBLES DE COUCHE INTERMÉDIAIRE (COUCHE 2 - MOYENNE)
402
+ for b in self.buildings_mid:
403
+ b["x"] -= 0.18
404
+ if b["x"] + b["w"] < 0:
405
+ b["x"] = self.W
406
+
407
+ x, w, h, col, roof = b["x"], b["w"], b["h"], b["col"], b["roof"]
408
+ top_y = horizon_y - h
409
+
410
+ # Dessin de l'immeuble de base
411
+ self.canvas_bg.create_rectangle(x, top_y, x + w, horizon_y, fill="#04000b", outline=col, width=1)
412
+
413
+ # Structures architecturales supplémentaires sur le toit
414
+ if roof == 1: # Stepped/Escalier
415
+ self.canvas_bg.create_rectangle(x + 5, top_y - 6, x + w - 5, top_y, fill="#04000b", outline=col, width=1)
416
+ elif roof == 3: # Dome/Coupole
417
+ self.canvas_bg.create_arc(x + 6, top_y - 12, x + w - 6, top_y + 4, start=0, extent=180, fill="#04000b", outline=col, width=1)
418
+
419
+ # 4. IMMEUBLES DU PREMIER PLAN DETAILLES (COUCHE 3 - RAPIDE)
420
+ for b in self.buildings_fg:
421
+ b["x"] -= 0.48
422
+ if b["x"] + b["w"] < 0:
423
+ b["x"] = self.W
424
+
425
+ x, w, h, col, roof = b["x"], b["w"], b["h"], b["col"], b["roof"]
426
+ sign = b.get("sign", "")
427
+ top_y = horizon_y - h
428
+
429
+ # Structure du toit de premier plan
430
+ if roof == 1: # Toit en escalier
431
+ self.canvas_bg.create_rectangle(x, top_y, x + w, horizon_y, fill="#060012", outline=col, width=1.5)
432
+ self.canvas_bg.create_rectangle(x + 4, top_y - 5, x + w - 4, top_y, fill="#060012", outline=col, width=1.5)
433
+ self.canvas_bg.create_rectangle(x + 8, top_y - 10, x + w - 8, top_y - 5, fill="#060012", outline=col, width=1.5)
434
+ elif roof == 2: # Toit avec mât et feu rouge clignotant
435
+ self.canvas_bg.create_rectangle(x, top_y, x + w, horizon_y, fill="#060012", outline=col, width=1.5)
436
+ cx = x + w // 2
437
+ self.canvas_bg.create_line(cx, top_y, cx, top_y - 15, fill=col, width=1.5)
438
+ # Clignotement du voyant de sécurité
439
+ flash_col = "#ff0000" if (int(time.time() * 3.5) % 2 == 0) else "#440000"
440
+ self.canvas_bg.create_oval(cx - 2.5, top_y - 17.5, cx + 2.5, top_y - 12.5, fill=flash_col, outline="")
441
+ elif roof == 3: # Coupole
442
+ self.canvas_bg.create_rectangle(x, top_y, x + w, horizon_y, fill="#060012", outline=col, width=1.5)
443
+ self.canvas_bg.create_arc(x + 4, top_y - 14, x + w - 4, top_y + 2, start=0, extent=180, fill="#060012", outline=col, width=1.5)
444
+ elif roof == 4: # Deux antennes / flèches
445
+ self.canvas_bg.create_rectangle(x, top_y, x + w, horizon_y, fill="#060012", outline=col, width=1.5)
446
+ self.canvas_bg.create_line(x + 6, top_y, x + 6, top_y - 12, fill=col, width=1.2)
447
+ self.canvas_bg.create_line(x + w - 6, top_y, x + w - 6, top_y - 12, fill=col, width=1.2)
448
+ # Feux de signalisation alternés
449
+ f1_col = "#ff0000" if (int(time.time() * 3) % 2 == 0) else "#440000"
450
+ f2_col = "#440000" if (int(time.time() * 3) % 2 == 0) else "#ff0000"
451
+ self.canvas_bg.create_oval(x + 4, top_y - 14, x + 8, top_y - 10, fill=f1_col, outline="")
452
+ self.canvas_bg.create_oval(x + w - 8, top_y - 14, x + w - 4, top_y - 10, fill=f2_col, outline="")
453
+ else: # Toit plat simple
454
+ self.canvas_bg.create_rectangle(x, top_y, x + w, horizon_y, fill="#060012", outline=col, width=1.5)
455
+
456
+ # Dessin des enseignes néons lumineuses publicitaires
457
+ if sign:
458
+ # Plaque de fond néon
459
+ self.canvas_bg.create_rectangle(
460
+ x + w//2 - 14, top_y + 12, x + w//2 + 14, top_y + 24,
461
+ fill="#000000", outline=self.FG_CYAN if sign == "SYS" else self.FG_MAG, width=1
462
+ )
463
+ self.canvas_bg.create_text(
464
+ x + w//2, top_y + 18, text=sign,
465
+ fill=self.FG_CYAN if sign == "SYS" else self.FG_MAG,
466
+ font=("Courier", 8, "bold")
467
+ )
468
+
469
+ # Fenêtres éclairées de façon dynamique à l'aide d'un PRNG déterministe basé sur l'abscisse
470
+ random.seed(int(x) + 400)
471
+ cols = w // 8
472
+ rows = h // 11
473
+ for r in range(1, rows):
474
+ for c in range(1, cols):
475
+ # Taux de fenêtres allumées (40%)
476
+ if random.random() < 0.40:
477
+ win_x = x + c * 8
478
+ win_y = top_y + r * 11
479
+ # Variantes de couleurs chaudes de fenêtres rétro
480
+ win_c = random.choice(["#ffff55", "#ffaa00", "#ffffff"])
481
+ self.canvas_bg.create_rectangle(
482
+ win_x, win_y, win_x + 2, win_y + 2,
483
+ fill=win_c, outline=""
484
+ )
485
+
486
+ # 5. ROUTE HIGHWAY PERSPECTIVE 3D ET TRAFFIC D'IMPULSION
487
+ # Ligne d'horizon néon magenta
488
+ self.canvas_bg.create_line(0, horizon_y, self.W, horizon_y, fill="#ff00ff", width=2)
489
+
490
+ # Dessin des lignes de fuite verticales (Perspective radiale)
491
+ for i in range(-12, 13):
492
+ self.canvas_bg.create_line(250 + i * 6, horizon_y, 250 + i * 45, max_y, fill="#003c55", width=1)
493
+
494
+ # Lignes d'horizon horizontales qui se rapprochent et s'accélèrent vers le bas (effet tunnel)
495
+ # L'incrément oscille entre 1.2 et 3.5 pour simuler des cycles d'accélération/décélération
496
+ if not hasattr(self, "_grid_speed"):
497
+ self._grid_speed = 1.2
498
+ self._grid_dir = 1
499
+ # Bascule de direction aux bornes pour rester dans une plage visuelle stable
500
+ if self._grid_speed >= 3.5:
501
+ self._grid_dir = -1
502
+ elif self._grid_speed <= 1.2:
503
+ self._grid_dir = 1
504
+ self._grid_speed += 0.012 * self._grid_dir
505
+ self.grid_offset = (self.grid_offset + self._grid_speed) % 12
506
+ y_lines = [130, 133, 137, 143, 152, 165, 183, 208, 240]
507
+ for idx, y in enumerate(y_lines[:-1]):
508
+ next_y = y_lines[idx+1]
509
+ interpolated_y = y + (next_y - y) * (self.grid_offset / 12.0)
510
+ if interpolated_y <= max_y:
511
+ self.canvas_bg.create_line(0, interpolated_y, self.W, interpolated_y, fill="#007799", width=1)
512
+
513
+ # Animation des flux de trafic néon (les "voitures" ou paquets binaires)
514
+ self.traffic_offset = (self.traffic_offset + 0.035) % 1.0
515
+ # Les véhicules se déplacent sur les voies clés du réseau routier virtuel
516
+ for lane_idx in [-5, -2, 2, 5]:
517
+ for car_p in range(3):
518
+ p = (self.traffic_offset + car_p / 3.0) % 1.0
519
+ p_curved = p * p # Distorsion quadratique perspective correcte
520
+
521
+ y_p = horizon_y + p_curved * (max_y - horizon_y)
522
+ x_p = 250 + lane_idx * (6 + p_curved * 39)
523
+
524
+ # Taille proportionnelle à la proximité de la caméra
525
+ size = 1 + p_curved * 4.5
526
+ color = self.FG_CYAN if lane_idx > 0 else self.FG_MAG
527
+
528
+ self.canvas_bg.create_rectangle(
529
+ x_p - size, y_p - size/2.5, x_p + size, y_p + size/2.5,
530
+ fill=color, outline=""
531
+ )
532
+
533
+
534
+ # 6. FILTRE CRT ET EFFET SCANLINE BALAYAGE VISUEL
535
+ self.scan_y = (self.scan_y + 3) % (self.H + 80)
536
+ self.canvas_bg.create_rectangle(
537
+ 0, self.scan_y - 20, self.W, self.scan_y,
538
+ fill=self.FG_GREEN, stipple="gray12", outline=""
539
+ )
540
+
541
+ # Quadrillage de lignes horizontales d'interfaçage moniteur cathodique
542
+ for y in range(0, self.CANVAS_H, 3):
543
+ self.canvas_bg.create_line(0, y, self.W, y, fill="#010a05")
544
+
545
+ # Prochain rafraîchissement d'affichage (~30 FPS)
546
+ self.root.after(33, self._tick_city)
547
+
548
+ def _tick_glitch(self) -> None:
549
+ """Gère les micro-glitchs textuels et clignotements chromatiques du titre du Keygen."""
550
+ try:
551
+ colors = [self.FG_GREEN, self.FG_CYAN, self.FG_MAG, self.FG_YEL, self.FG_RED]
552
+ self.title_lbl.config(fg=random.choice(colors))
553
+ t = "🚀 OVERMIND BEARER KEYGEN 2 0 0 0 🚀"
554
+ # Une chance sur quatre d'avoir un caractère altéré/cryptique temporaire
555
+ if random.random() < 0.25:
556
+ idx = random.randint(0, len(t) - 1)
557
+ t = t[:idx] + random.choice("█▓▒░*#@&$") + t[idx + 1:]
558
+ self.title_lbl.config(text=t)
559
+ except Exception:
560
+ pass
561
+ self.root.after(160, self._tick_glitch)
562
+
563
+ def _build_ui(self) -> None:
564
+ """Initialise, positionne et configure tous les widgets Tkinter de l'application."""
565
+ # Canvas principal recevant le rendu de la ville
566
+ self.canvas_bg = tk.Canvas(
567
+ self.root, width=self.W, height=self.CANVAS_H,
568
+ bg="#020005", highlightthickness=0, bd=0
569
+ )
570
+ self.canvas_bg.place(x=0, y=26)
571
+
572
+ # Barre de titre supérieure permettant de faire glisser la fenêtre borderless
573
+ title_bar = tk.Frame(self.root, bg=self.BG_TITLE, height=26)
574
+ title_bar.place(x=0, y=0, width=self.W, height=26)
575
+ title_bar.bind("<ButtonPress-1>", self._on_press)
576
+ title_bar.bind("<ButtonRelease-1>", self._on_release)
577
+ title_bar.bind("<B1-Motion>", self._on_motion)
578
+
579
+ self.title_lbl = tk.Label(
580
+ title_bar, text="🚀 OVERMIND BEARER KEYGEN 2 0 0 0 🚀",
581
+ fg=self.FG_GREEN, bg=self.BG_TITLE,
582
+ font=("Courier", 11, "bold"),
583
+ )
584
+ self.title_lbl.pack(side="left", padx=8)
585
+
586
+ # Bouton Fermer [X]
587
+ close_btn = tk.Button(
588
+ self.root, text="[X]", command=self._close,
589
+ fg=self.FG_RED, bg=self.BG_TITLE,
590
+ activeforeground="#ffffff", activebackground="#660000",
591
+ bd=1, relief="raised",
592
+ font=("Courier", 10, "bold"),
593
+ width=4, cursor="hand2",
594
+ )
595
+ close_btn.place(x=self.W - 38, y=2)
596
+
597
+ # Ruban de texte défilant de pied de scène (Marquee)
598
+ self.marquee_text = " *** OVERMIND BEARER KEYGEN v1.0 *** CRAFTED BY DEMON-CORP *** FORGE YOUR KEY SECURELY *** FOR OVERMIND BEARER USE *** GREETZ TO ALL CO-AGENTS IN THE GRID *** "
599
+ self.foot_lbl = tk.Label(
600
+ self.root,
601
+ text=self.marquee_text,
602
+ fg=self.FG_MAG, bg=self.BG_DEEP,
603
+ font=("Courier", 8, "bold")
604
+ )
605
+ self.foot_lbl.place(x=10, y=243)
606
+
607
+ # Zone d'affichage des clés générées (Formulaire enclavé)
608
+ self.key_frame = tk.Frame(self.root, bg=self.BG_PANEL, bd=1, relief="sunken")
609
+ self.key_frame.place(x=10, y=264, width=self.W - 20, height=52)
610
+ self.key_frame.columnconfigure(1, weight=1)
611
+
612
+ # Label et Entry de l'Adresse Ethereum
613
+ tk.Label(self.key_frame, text="ADDRESS:", fg=self.FG_CYAN, bg=self.BG_PANEL, font=("Courier", 8, "bold")).grid(row=0, column=0, sticky="w", padx=5, pady=2)
614
+ self.addr_var = tk.StringVar(value="0x........................................")
615
+ self.addr_entry = tk.Entry(self.key_frame, textvariable=self.addr_var, fg=self.FG_GREEN, bg="#050a06", insertbackground=self.FG_GREEN, font=("Courier", 8, "bold"), bd=1, width=52)
616
+ self.addr_entry.grid(row=0, column=1, padx=5, pady=2, sticky="ew")
617
+ self.addr_entry.config(state="readonly")
618
+
619
+ # Label et Entry de la Clé Privée (Verrouillée en Cyan `#00ffff` sur fond sombre)
620
+ tk.Label(self.key_frame, text="PRIVATE:", fg=self.FG_CYAN, bg=self.BG_PANEL, font=("Courier", 8, "bold")).grid(row=1, column=0, sticky="w", padx=5, pady=2)
621
+ self.priv_var = tk.StringVar(value="")
622
+ self.priv_entry = tk.Entry(self.key_frame, textvariable=self.priv_var, fg=self.FG_CYAN, bg="#050a0a", insertbackground=self.FG_CYAN, font=("Courier", 8, "bold"), bd=1, show="*", width=52)
623
+ self.priv_entry.grid(row=1, column=1, padx=5, pady=2, sticky="ew")
624
+ self.priv_entry.config(state="readonly")
625
+
626
+ # Conteneur des boutons d'actions inférieurs
627
+ btn_frame = tk.Frame(self.root, bg=self.BG_DEEP)
628
+ btn_frame.place(x=10, y=320)
629
+
630
+ # Usine à boutons stylisés
631
+ def btn(parent, text, cmd, fg=None, width=15):
632
+ return tk.Button(
633
+ parent, text=text, command=cmd,
634
+ fg=fg or self.FG_GREEN, bg=self.BG_TITLE,
635
+ activeforeground="#ffffff", activebackground="#333333",
636
+ bd=2, relief="raised",
637
+ font=("Courier", 10, "bold"),
638
+ width=width, padx=6, pady=4,
639
+ cursor="hand2",
640
+ )
641
+
642
+ self.gen_btn = btn(btn_frame, "[ GENERATE ]", self._start_generation)
643
+ self.gen_btn.pack(side="left", padx=5)
644
+
645
+ self.copy_btn = btn(btn_frame, "[ COPY PRIVATE ]", self._copy_private, fg="#ffcc00")
646
+ self.copy_btn.pack(side="left", padx=5)
647
+ self.copy_btn.config(state="disabled")
648
+
649
+ self.music_btn = btn(btn_frame, "[ MUSIC: ON ]", self._toggle_music, fg=self.FG_GREEN)
650
+ self.music_btn.pack(side="left", padx=5)
651
+
652
+ def _show_splash(self) -> None:
653
+ """Affiche un écran de chargement rétro 'LOADING OVERMIND OS...' pendant 1.5s."""
654
+ # Overlay plein écran sur le canvas de scène
655
+ self._splash_overlay = tk.Frame(self.root, bg="#000000", bd=2, relief="ridge",
656
+ highlightthickness=2, highlightbackground=self.FG_CYAN)
657
+ self._splash_overlay.place(x=4, y=30, width=self.W - 8, height=self.CANVAS_H - 8)
658
+
659
+ tk.Label(
660
+ self._splash_overlay, text=">>> LOADING OVERMIND OS v1.0 <<<",
661
+ fg=self.FG_GREEN, bg="#000000",
662
+ font=("Courier", 10, "bold"),
663
+ ).pack(pady=(40, 8))
664
+
665
+ # Progress bar textuelle
666
+ self._splash_bar_lbl = tk.Label(
667
+ self._splash_overlay, text="[ ] 0%",
668
+ fg=self.FG_YEL, bg="#000000",
669
+ font=("Courier", 10, "bold"),
670
+ )
671
+ self._splash_bar_lbl.pack(pady=4)
672
+
673
+ tk.Label(
674
+ self._splash_overlay, text="Mounting entropy pool...",
675
+ fg=self.FG_MAG, bg="#000000",
676
+ font=("Courier", 8, "bold"),
677
+ ).pack(pady=(20, 0))
678
+
679
+ # Démarre l'animation de la progress bar
680
+ self._update_splash(0)
681
+
682
+ def _update_splash(self, pct: int) -> None:
683
+ """Avance la jauge LOADING et détruit l'overlay à 100%."""
684
+ try:
685
+ bar = "[" + "█" * (pct // 5) + " " * (20 - pct // 5) + "]"
686
+ self._splash_bar_lbl.config(text=f"{bar} {pct:3d}%")
687
+ except Exception:
688
+ return
689
+ if pct >= 100:
690
+ # Splash terminé -> on retire l'overlay
691
+ try:
692
+ self._splash_overlay.destroy()
693
+ except Exception:
694
+ pass
695
+ return
696
+ self.root.after(30, self._update_splash, pct + 2)
697
+
698
+ def _glitch_rgb_shift(self) -> None:
699
+ """Effet VHS : décale temporairement les couleurs de l'overlay frame pendant 150ms."""
700
+ try:
701
+ # On crée 3 copies translatées du canvas de scène en R/G/B (effet chromatisme)
702
+ w, h = self.W, self.CANVAS_H
703
+ # 3 lignes fines de couleurs pures qui flashent (simule un shift RGB discret)
704
+ self.canvas_bg.create_rectangle(2, 0, w, 3, fill="#ff0000", stipple="gray25", outline="", tags="rgb")
705
+ self.canvas_bg.create_rectangle(-2, 0, w - 4, 3, fill="#00ff00", stipple="gray25", outline="", tags="rgb")
706
+ self.canvas_bg.create_rectangle(0, 0, w, 3, fill="#0000ff", stipple="gray25", outline="", tags="rgb")
707
+ # Auto-cleanup
708
+ self.root.after(150, lambda: self.canvas_bg.delete("rgb"))
709
+ except Exception:
710
+ pass
711
+
712
+ def _start_generation(self) -> None:
713
+ """Déclenche la routine asynchrone de génération et de reveal de la paire de clés."""
714
+ if self.gen_btn.cget("state") == "disabled":
715
+ return
716
+ self.gen_btn.config(state="disabled")
717
+ self.copy_btn.config(state="disabled")
718
+ self.priv_var.set("")
719
+ self.addr_var.set("0x" + "•" * 40)
720
+ t = threading.Thread(target=self._scan_then_reveal, daemon=True)
721
+ t.start()
722
+
723
+ def _scan_then_reveal(self) -> None:
724
+ """Génère la clé, joue le signal sonore basse-fréquence, et révèle les caractères l'un après l'autre."""
725
+ try:
726
+ key = gen_one()
727
+ except Exception:
728
+ self.root.after(0, lambda: self.gen_btn.config(state="normal"))
729
+ return
730
+
731
+ self.current_key = key
732
+ addr = key["address"]
733
+ priv = key["private_key"]
734
+
735
+ # Signal de transition sonore non strident
736
+ self.synth.reveal_beep()
737
+
738
+ # Révélation progressive de l'Adresse
739
+ for i in range(len(addr)):
740
+ shown = addr[: i + 1] + "•" * (len(addr) - i - 1)
741
+ self.root.after(0, lambda s=shown: self.addr_var.set(s))
742
+ time.sleep(0.015)
743
+ self.root.after(0, lambda: self.addr_var.set(addr))
744
+
745
+ # Flash lumineux d'impact rétro sur le canvas
746
+ self.root.after(0, self._flash)
747
+ # Glitch VHS (décalage RGB) qui accompagne le reveal
748
+ self.root.after(0, self._glitch_rgb_shift)
749
+
750
+ # Révélation progressive de la Clé Privée
751
+ for i in range(len(priv)):
752
+ shown = priv[: i + 1] + "•" * (len(priv) - i - 1)
753
+ self.root.after(0, lambda s=shown: self.priv_var.set(s))
754
+ time.sleep(0.02)
755
+
756
+ # Déverrouillage des boutons d'actions
757
+ self.root.after(0, lambda: self.gen_btn.config(state="normal"))
758
+ self.root.after(0, lambda: self.copy_btn.config(state="normal"))
759
+
760
+ def _flash(self) -> None:
761
+ """Crée un flash lumineux stroboscopique vert transparent d'une fraction de seconde."""
762
+ fl = self.canvas_bg.create_rectangle(
763
+ 0, 0, self.W, self.CANVAS_H, fill=self.FG_GREEN, stipple="gray12", outline=""
764
+ )
765
+ self.root.after(120, lambda: self.canvas_bg.delete(fl))
766
+
767
+ def _copy_private(self) -> None:
768
+ """Copie la paire {address, private_key} en JSON dans le presse-papiers système.
769
+
770
+ Format: {"address":"0x...","private_key":"0x..."} (une seule ligne, prêt à coller).
771
+ """
772
+ if not self.current_key:
773
+ return
774
+ # JSON une ligne, séparateurs serrés, prêt à coller dans un autre outil
775
+ payload = json.dumps(
776
+ {"address": self.current_key["address"],
777
+ "private_key": self.current_key["private_key"]},
778
+ separators=(",", ":"),
779
+ )
780
+ try:
781
+ self.root.clipboard_clear()
782
+ self.root.clipboard_append(payload)
783
+ self.root.update()
784
+ except Exception:
785
+ pass
786
+ # Feedback visuel temporaire sur le bouton
787
+ orig = "[ COPY PRIVATE ]"
788
+ self.copy_btn.config(text="[ COPIED! ]", fg=self.FG_GREEN)
789
+ self.root.after(1500, lambda: self.copy_btn.config(text=orig, fg="#ffcc00"))
790
+
791
+ def _toggle_music(self) -> None:
792
+ """Active/Désactive la lecture du module chiptune de fond."""
793
+ playing = self.synth.toggle()
794
+ if playing:
795
+ self.music_btn.config(text="[ MUSIC: ON ]", fg=self.FG_GREEN)
796
+ else:
797
+ self.music_btn.config(text="[ MUSIC: OFF ]", fg=self.FG_RED)
798
+
799
+ def start_marquee(self) -> None:
800
+ """Démarre le fil de texte défilant dans un thread autonome."""
801
+ def scroll():
802
+ text = self.marquee_text
803
+ while True:
804
+ try:
805
+ text = text[1:] + text[0]
806
+ self.foot_lbl.config(text=text)
807
+ time.sleep(0.1)
808
+ except Exception:
809
+ break
810
+ t = threading.Thread(target=scroll, daemon=True)
811
+ t.start()
812
+
813
+ def _close(self) -> None:
814
+ """Stoppe les audios et détruit la fenêtre Tkinter pour quitter proprement."""
815
+ self.synth.stop()
816
+ try:
817
+ self.root.destroy()
818
+ except Exception:
819
+ pass
820
+
821
+
822
+ # =========================================================================
823
+ # LOBBY DE LANCEMENT ET EXECUTION CLI / GUI
824
+ # =========================================================================
825
+ def run_gui() -> None:
826
+ root = tk.Tk()
827
+ app = KeygenApp(root)
828
+ app.start_marquee()
829
+ root.mainloop()
830
+
831
+
832
+ def main() -> int:
833
+ p = argparse.ArgumentParser(
834
+ description="OVERMIND BEARER KEYGEN 2000 — Générateur rétro sécurisé d'adresses et clés privées EVM"
835
+ )
836
+ p.add_argument("--cli", action="store_true", help="Forcer le mode console CLI")
837
+ p.add_argument("--count", type=int, default=1, help="Nombre de clés à générer (mode CLI)")
838
+ p.add_argument("--out", type=str, default=None, help="Exporter les résultats en format JSON dans un fichier")
839
+ p.add_argument("--no-0x", action="store_true", help="Générer la clé privée brute sans préfixe 0x")
840
+ args = p.parse_args()
841
+
842
+ # Si Tkinter est indisponible ou si l'utilisateur spécifie explicitement le mode CLI
843
+ if not GUI_AVAILABLE or args.cli or args.out or args.count > 1:
844
+ if args.count < 1 or args.count > 1000:
845
+ sys.stderr.write("Erreur : --count doit être compris entre 1 et 1000.\n")
846
+ return 1
847
+ keys: list[dict[str, str]] = [gen_one(no_0x=args.no_0x) for _ in range(args.count)]
848
+ payload: Any = keys[0] if args.count == 1 else keys
849
+ text = json.dumps(payload, indent=2)
850
+
851
+ if args.out:
852
+ tmp = args.out + ".tmp"
853
+ with open(tmp, "w", encoding="utf-8") as f:
854
+ f.write(text)
855
+ f.write("\n")
856
+ try:
857
+ os.chmod(tmp, 0o600) # Restreindre la lecture/écriture au propriétaire de la machine
858
+ except Exception:
859
+ pass
860
+ os.replace(tmp, args.out)
861
+ sys.stderr.write(f"Succès : {len(keys)} clé(s) écrite(s) dans '{args.out}' (droits 0600).\n")
862
+ else:
863
+ sys.stdout.write(text + "\n")
864
+ return 0
865
+
866
+ run_gui()
867
+ return 0
868
+
869
+
870
+ if __name__ == "__main__":
871
+ sys.exit(main())