Загрузка данных
import os
import secrets
import string
from datetime import datetime
MIN_LEN = 6 # Минимальная длина пароля
MAX_LEN = 128 # Максимальная длина пароля
DEFAULT_LEN = 16 # Длина по умолчанию
HISTORY_FILE = "password_history.txt" # Файл истории
HISTORY_SIZE = 5 # Максимум записей истории
SPECIALS = "!@#$%^&*()-_=+[]{}|;:,.<>?" # Спецсимволы
def ask_yes_no(prompt, default="y"):
"""Запрос ответа y/n. Возвращает bool."""
val = input(prompt).strip().lower()
if not val:
val = default
return val == "y"
def ask_length():
"""Запрашивает корректную длину пароля."""
while True:
val = input(f"Длина пароля [{MIN_LEN}–{MAX_LEN}, по умолчанию {DEFAULT_LEN}]: ").strip()
if not val:
return DEFAULT_LEN
try:
num = int(val)
if MIN_LEN <= num <= MAX_LEN:
return num
print(f"[ОШИБКА] Длина должна быть от {MIN_LEN} до {MAX_LEN}.")
except ValueError:
print("[ОШИБКА] Введите целое число.")
def select_sets():
"""Выбор наборов символов."""
while True:
sets = []
enabled = []
if ask_yes_no("Включить строчные буквы? (y/n) [y]: "):
sets.append(string.ascii_lowercase)
enabled.append("lower")
if ask_yes_no("Включить прописные буквы? (y/n) [y]: "):
sets.append(string.ascii_uppercase)
enabled.append("upper")
if ask_yes_no("Включить цифры? (y/n) [y]: "):
sets.append(string.digits)
enabled.append("digits")
if ask_yes_no("Включить спецсимволы? (y/n) [y]: "):
sets.append(SPECIALS)
enabled.append("special")
if sets:
return sets, enabled
print("[ОШИБКА] Необходимо выбрать хотя бы один набор символов")
def generate_password(length, sets):
"""Генерирует пароль с гарантией символа из каждого набора."""
chars = [secrets.choice(s) for s in sets] # гарантия включения
pool = "".join(sets)
chars.extend(secrets.choice(pool) for _ in range(length - len(chars)))
secrets.SystemRandom().shuffle(chars) # перемешивание
return "".join(chars)
def copy_to_clipboard(password):
"""Копирует пароль в буфер обмена."""
try:
import tkinter as tk
root = tk.Tk()
root.withdraw()
root.clipboard_clear()
root.clipboard_append(password)
root.update()
root.destroy()
print("[INFO] Пароль скопирован в буфер обмена.")
except Exception:
print("[INFO] Копирование недоступно: модуль tkinter не найден")
def save_history(password, length, enabled):
"""Сохраняет пароль в историю с ротацией до 5 записей."""
try:
lines = []
if os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE, "r", encoding="utf-8") as f:
lines = [x.rstrip("\n") for x in f]
record = (
f"{datetime.now():%Y-%m-%d %H:%M:%S} | длина: {length} | "
f"наборы: {enabled} | пароль: {password}"
)
lines.append(record)
lines = lines[-HISTORY_SIZE:] # ротация истории
with open(HISTORY_FILE, "w", encoding="utf-8") as f:
f.write("\n".join(lines))
print(f"[INFO] Пароль сохранён в {HISTORY_FILE}.")
except Exception as e:
print(f"[ОШИБКА] Не удалось сохранить файл: {e}")
def main():
"""Основной цикл программы."""
print("Генератор безопасных паролей")
print("=" * 29)
while True:
length = ask_length()
sets, enabled = select_sets()
password = generate_password(length, sets)
print("\nВаш пароль:")
print("-" * 29)
print(password)
print("-" * 29)
if ask_yes_no("Скопировать в буфер обмена? (y/n) [y]: "):
copy_to_clipboard(password)
if ask_yes_no("Сохранить в историю? (y/n) [n]: ", "n"):
save_history(password, length, enabled)
if not ask_yes_no("Сгенерировать ещё один пароль? (y/n) [n]: ", "n"):
print("До свидания!")
break
if __name__ == "__main__":
main()