wysylka maili

This commit is contained in:
Mateusz Gruszczyński
2025-05-16 23:11:46 +02:00
parent 528f31bd2e
commit c311096e05
4 changed files with 311 additions and 21 deletions

159
app.py
View File

@ -17,6 +17,11 @@ from tabulate import tabulate
import xlsxwriter
from collections import defaultdict
import subprocess
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import time
# Redis - baza 5
redis_client = redis.Redis(host='localhost', port=6379, db=5, decode_responses=True)
@ -149,9 +154,89 @@ def confirm_delete():
def days_to_years(days):
return round(days / 365, 1)
def get_smtp_config():
required_keys = ["SMTP_HOST", "SMTP_PORT", "SMTP_USER", "SMTP_PASSWORD"]
config = {key: os.getenv(key) for key in required_keys}
missing = [k for k, v in config.items() if not v]
if missing:
raise ValueError(f"❌ Brakuje wymaganych zmiennych SMTP w pliku .env: {', '.join(missing)}")
return {
"host": config["SMTP_HOST"],
"port": config["SMTP_PORT"],
"user": config["SMTP_USER"],
"password": config["SMTP_PASSWORD"]
}
def send_email_batch(users, smtp_config, mails_per_pack=100, time_per_pack=60, dry_run=False):
import os
template_path = "mail_template.html"
if not os.path.exists(template_path):
print(f"❌ Brak pliku szablonu: {template_path}")
return
try:
with open(template_path, "r", encoding="utf-8") as f:
template = f.read()
except Exception as e:
print(f"❌ Błąd podczas odczytu szablonu HTML: {e}")
return
try:
smtp = smtplib.SMTP(smtp_config["host"], int(smtp_config["port"]))
smtp.starttls()
smtp.login(smtp_config["user"], smtp_config["password"])
except Exception as e:
print(f"❌ Błąd połączenia z serwerem SMTP: {e}")
return
print(f"📨 Rozpoczynam wysyłkę {len(users)} maili...")
for i in tqdm(range(0, len(users), mails_per_pack), desc="Wysyłanie emaili"):
batch = users[i:i + mails_per_pack]
for user in batch:
if dry_run:
print(f"🔔 [TRYB TESTOWY] Mail do: {user['mail']}")
continue
try:
msg = MIMEMultipart()
msg['From'] = smtp_config["user"]
msg['To'] = user['mail']
msg['Subject'] = "Twoje konto w unitraklub.pl"
body = template.replace("@user", user['name']).replace(
"@rejestracja", datetime.datetime.fromtimestamp(user['created']).strftime('%Y-%m-%d'))
msg.attach(MIMEText(body, 'html'))
smtp.send_message(msg)
time.sleep(0.5) # opcjonalne mikroopóźnienie między mailami
except Exception as e:
print(f"⚠️ Błąd wysyłki do {user['mail']}: {e}")
if i + mails_per_pack < len(users):
print(f"⏸ Przerwa {time_per_pack} sekund między paczkami...")
time.sleep(time_per_pack)
smtp.quit()
print("✅ Wysyłka zakończona.")
def validate_smtp_config(config):
required = ['host', 'port', 'user', 'password']
for key in required:
if not config.get(key):
raise ValueError(f"❌ Brakuje wartości SMTP dla: {key}")
def main():
signal.signal(signal.SIGINT, lambda s, f: sys.exit("\n🛑 Przerwano przez użytkownika."))
load_dotenv()
try:
smtp_config = get_smtp_config()
except ValueError as e:
print(str(e))
sys.exit(1)
parser = argparse.ArgumentParser(
description="Drupal 6 user cleanup tool",
@ -176,32 +261,70 @@ def main():
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument('--host')
parser.add_argument('--user')
parser.add_argument('--password')
parser.add_argument('--database')
parser.add_argument('--days-inactive', type=int)
parser.add_argument('--host', help='Adres hosta bazy danych (można ustawić w .env jako DB_HOST)')
parser.add_argument('--user', help='Użytkownik bazy danych (lub DB_USER z .env)')
parser.add_argument('--password', help='Hasło do bazy danych (lub DB_PASSWORD z .env)')
parser.add_argument('--database', help='Nazwa bazy danych (lub DB_NAME z .env)')
parser.add_argument('--days-inactive', type=int,
help='Minimalna liczba dni nieaktywności, po której użytkownik uznawany jest za nieaktywny')
parser.add_argument('--dry-run', action='store_true', default=None,
help='Tryb podglądu (domyślny jeśli nie podano --delete)')
parser.add_argument('--delete', action='store_true')
parser.add_argument('--validate', action='store_true')
parser.add_argument('--flush-cache', action='store_true')
parser.add_argument('--export-excel', action='store_true')
parser.add_argument('--report-domains', action='store_true')
help='Tryb podglądu: nie wykonuje żadnych zmian, tylko raportuje')
parser.add_argument('--delete', action='store_true',
help='Usuń użytkowników, którzy spełniają kryteria filtrowania')
parser.add_argument('--validate', action='store_true',
help='Tylko sprawdź poprawność adresów e-mail (bez usuwania)')
parser.add_argument('--flush-cache', action='store_true',
help='Wyczyść cache rekordów MX w Redisie')
parser.add_argument('--export-excel', action='store_true',
help='Zapisz wynik filtrowania użytkowników także do pliku Excel (.xlsx)')
parser.add_argument('--report-domains', action='store_true',
help='Wygeneruj raport ilości użytkowników według domen e-mail')
parser.add_argument('--veteran-year', type=int, default=2012,
help='Minimalny rok rejestracji konta do uznania za stare (domyślnie: 2012)')
help='Rok, przed którym konto uznawane jest za stare/weterana (domyślnie: 2012)')
parser.add_argument('--recent-login-days', type=int, default=1095,
help='Ile dni wstecz ostatnie logowanie czyni konto aktywnym (domyślnie: 1095)')
help='Ile dni wstecz uznaje się zalogowanego weterana za aktywnego (domyślnie: 1095)')
parser.add_argument('--show-table', action='store_true',
help='Wyświetl tabelę z listą użytkowników do usunięcia')
help='Wyświetl tabelę użytkowników spełniających kryteria do usunięcia')
parser.add_argument('--drupal-path',
help="Ścieżka do katalogu Drupala (można też podać przez .env jako DRUPAL_PATH)")
help='Ścieżka do katalogu Drupala (można ustawić przez .env jako DRUPAL_PATH)')
parser.add_argument('--send-test', metavar="EMAIL",
help='Wyślij testowego maila z szablonu na wskazany adres e-mail')
parser.add_argument('--send-mails', action='store_true',
help='Wyślij powiadomienia e-mail do użytkowników z listy kandydatów')
parser.add_argument('--mails-per-pack', type=int, default=100,
help='Ile e-maili wysyłać w jednej paczce (domyślnie: 100)')
parser.add_argument('--time-per-pack', type=int, default=60,
help='Ile sekund czekać między paczkami maili (domyślnie: 60 sek.)')
args = parser.parse_args()
if args.send_test:
test_user = {
'name': 'Testowy Użytkownik',
'mail': args.send_test,
'created': int(datetime.datetime.now().timestamp()) - (86400 * 365 * 2) # 2 lata temu
}
print(f"📬 Wysyłka testowego maila na: {test_user['mail']}")
send_email_batch([test_user], smtp_config, mails_per_pack=1, time_per_pack=0, dry_run=False)
return
if not args.drupal_path:
args.drupal_path = os.getenv("DRUPAL_PATH")
@ -280,9 +403,15 @@ def main():
], headers=["UID", "Login", "E-mail", "Ostatnie log.", "Rejestracja", "Punkty", "Nieaktywny", "E-mail OK", "Tymczasowy"], tablefmt="fancy_grid"))
export_to_csv(final_candidates)
if args.export_excel:
export_to_excel(final_candidates)
if args.send_mails:
send_email_batch(final_candidates, smtp_config, args.mails_per_pack, args.time_per_pack)
print("\n📋 Parametry filtrowania:")
if args.days_inactive:
print(f"- Nieaktywni: brak logowania przez ≥ {args.days_inactive} dni (~{days_to_years(args.days_inactive)} lat)")