From e29162b47032d64dc752422aa1df4b179e61f0f7 Mon Sep 17 00:00:00 2001 From: Florian Sylvain Date: Sat, 22 Mar 2025 22:37:21 +0100 Subject: [PATCH] Initial commit --- .gitignore | 175 ++++++++++++++++++++++++++++++++++++++++++ main.py | 193 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 6 ++ 3 files changed, 374 insertions(+) create mode 100644 .gitignore create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6504c80 --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +.cache.* +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc diff --git a/main.py b/main.py new file mode 100644 index 0000000..bab8c0a --- /dev/null +++ b/main.py @@ -0,0 +1,193 @@ +import os +import time +import hashlib +import secrets +import random +import socket +import platform +import psutil +import uuid +import threading +import datetime +import multiprocessing +from progress.bar import Bar +from concurrent.futures import ProcessPoolExecutor +import openmeteo_requests +import requests_cache +from retry_requests import retry + +def fetch_weather_entropy(): + """Récupère des données météo comme source supplémentaire d'entropie""" + try: + # Configuration du client API Open-Meteo avec cache et nouvelle tentative en cas d'erreur + cache_session = requests_cache.CachedSession('.cache', expire_after=60) # Courte durée de cache + retry_session = retry(cache_session, retries=2, backoff_factor=0.2) # Nouvelle tentative rapide + openmeteo = openmeteo_requests.Client(session=retry_session) + + # Demande de données météo avec un délai d'expiration + url = "https://api.open-meteo.com/v1/forecast" + params = { + "latitude": 50.4, + "longitude": 1.83, + "hourly": ["temperature_2m", "relative_humidity_2m", "wind_speed_10m", + "visibility", "precipitation", "cloud_cover"], + "timezone": "auto" + } + + responses = openmeteo.weather_api(url, params=params) + response = responses[0] + + # Extraction de toutes les valeurs numériques des prévisions horaires + entropy_values = [] + hourly = response.Hourly() + + # Traitement de chaque variable météorologique + for i in range(len(params["hourly"])): + var_values = hourly.Variables(i).ValuesAsNumpy() + entropy_values.extend(var_values.tolist()) + + # Création d'un hash de toutes les données météorologiques + weather_str = "".join([str(v) for v in entropy_values]) + weather_hash = hashlib.sha256(weather_str.encode()).hexdigest() + + return weather_hash + except Exception as e: + # If anything fails, return a timestamp-based fallback + fallback = f"weather_fallback_{time.time()}_{random.getrandbits(64)}" + return hashlib.sha256(fallback.encode()).hexdigest() + +def initialize_entropy_pool(): + """Crée un pool d'entropie de base avec des sources qui ne changent pas rapidement""" + weather_entropy = fetch_weather_entropy() + + return [ + str(os.urandom(32)), # 32 octets d'aléa du système + str(secrets.token_bytes(32)), # Autre source cryptographique + socket.gethostname(), # Nom de l'hôte + str(platform.system_alias(platform.system(), platform.release(), platform.version())), + str(uuid.getnode()), # Adresse MAC + str(multiprocessing.cpu_count()), # Nombre de CPU + "".join([str(v) for v in psutil.disk_partitions()]), # Configuration des disques + str(hash(frozenset(os.environ.items()))), # Variables d'environnement, + weather_entropy, # Données météorologiques + ] + +def generate_seed(base_pool=None): + """Génère une graine aléatoire en combinant différentes sources d'entropie""" + # Utiliser le pool de base si fourni, sinon créer des sources complètes + if base_pool: + entropy_sources = base_pool.copy() + else: + entropy_sources = [] + + # Ajouter les sources dynamiques qui changent à chaque appel + dynamic_sources = [ + str(time.time()), # Timestamp actuel + hashlib.sha256(str(time.perf_counter()).encode()).hexdigest(), # Timer haute précision hashé + str(os.getpid()), # ID du processus + str(psutil.cpu_percent(interval=0.01)), # Utilisation CPU (réduit à 0.01) + str(psutil.virtual_memory().percent), # Utilisation mémoire + str(psutil.disk_usage('/').percent), # Utilisation disque + str(random.getrandbits(256)), # Bits aléatoires du générateur par défaut + str(datetime.datetime.now().microsecond), # Microsecondes actuelles + str(threading.active_count()), # Nombre de threads actifs + str(sum(psutil.cpu_times())), # Temps CPU cumulés + str(psutil.net_io_counters().bytes_sent if hasattr(psutil, 'net_io_counters') else 0), # Trafic réseau + str(id({})), # Adresse mémoire d'un nouvel objet + ] + + entropy_sources.extend(dynamic_sources) + + random.shuffle(entropy_sources) + + # Concaténer et hacher plusieurs fois avec différents algorithmes + entropy_str = "".join(entropy_sources) + intermediate_hash = hashlib.sha512(entropy_str.encode()).digest() + intermediate_hash = hashlib.blake2b(intermediate_hash).digest() + + # Utiliser un hachage final pour obtenir un entier + final_hash = hashlib.sha256(intermediate_hash).hexdigest() + seed = int(final_hash, 16) + + return seed + +def faire_tirage(base_pool=None): + """Génère un tirage avec 5 numéros et 2 étoiles en utilisant une graine aléatoire""" + # Génération de nouveaux nombres avec une nouvelle graine + seed = generate_seed(base_pool) + random.seed(seed) + numeros = random.sample(range(1, 51), 5) + etoiles = random.sample(range(1, 13), 2) + return { + "graine": seed, + "numeros": sorted(numeros), + "etoiles": sorted(etoiles) + } + +def worker_process(num_tirages, base_pool): + """Fonction exécutée par chaque processus travailleur""" + local_tirages = [] + for _ in range(num_tirages): + local_tirages.append(faire_tirage(base_pool)) + return local_tirages + +def generate_draws(num_tirages): + """Génère plusieurs tirages en parallèle""" + # Initialiser le pool d'entropie de base + base_pool = initialize_entropy_pool() + + # Déterminer le nombre optimal de processus + num_processes = min(os.cpu_count() or 4, 4) # Limiter à un nombre raisonnable + + # Répartir le travail entre les processus + tirages_par_processus = num_tirages // num_processes + remainder = num_tirages % num_processes + + # Préparer les tâches pour chaque processus + tasks = [] + for i in range(num_processes): + count = tirages_par_processus + (1 if i < remainder else 0) + if count > 0: + tasks.append((count, base_pool)) + + bar = Bar('Progression', max=num_tirages, suffix='%(percent)d%%') + + # Exécuter les tâches en parallèle + tirages = [] + with ProcessPoolExecutor(max_workers=num_processes) as executor: + futures = [executor.submit(worker_process, count, bp) for count, bp in tasks] + for future in futures: + batch = future.result() + tirages.extend(batch) + bar.next(len(batch)) + + bar.finish() + return tirages + +if __name__ == "__main__": + print("Génération de 100 tirages différents...") + tirages = generate_draws(100) + + # Mélange final pour le choix du tirage avec une nouvelle graine + base_pool = initialize_entropy_pool() + random.seed(generate_seed(base_pool)) + tirage_choisi = random.choice(tirages) + + print("\n===== RÉSULTAT FINAL =====") + print("Tirage sélectionné parmi les 100 générés") + print("Graine utilisée :", tirage_choisi["graine"]) + print("Numéros :", tirage_choisi["numeros"]) + print("Étoiles :", tirage_choisi["etoiles"]) + + # Demander à l'utilisateur s'il souhaite afficher tous les tirages + afficher_tous = input("\nVoulez-vous afficher tous les tirages générés? (o/n): ").lower().strip() + if afficher_tous in ['o', 'oui', 'y', 'yes']: + print("\n===== TOUS LES TIRAGES =====") + for i, tirage in enumerate(tirages, 1): + print(f"Tirage {i}:") + print(f" Numéros: {tirage['numeros']}") + print(f" Étoiles: {tirage['etoiles']}") + print(f" Graine: {tirage['graine']}") + print("-" * 30) + + input("\nAppuyez sur Entrée pour quitter...") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3e4a52b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +futures==3.0.5 +openmeteo_requests==1.3.0 +progress==1.6 +psutil==7.0.0 +requests-cache==1.2.1 +retry-requests==2.0.0