zmiany, temp maile, csv browser
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -6,3 +6,5 @@ __pycache__/
 | 
				
			|||||||
*.pyc
 | 
					*.pyc
 | 
				
			||||||
user_cleanup_results_*
 | 
					user_cleanup_results_*
 | 
				
			||||||
venv
 | 
					venv
 | 
				
			||||||
 | 
					exports
 | 
				
			||||||
 | 
					logs
 | 
				
			||||||
							
								
								
									
										96
									
								
								app.py
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								app.py
									
									
									
									
									
								
							@@ -21,20 +21,25 @@ import smtplib
 | 
				
			|||||||
from email.mime.multipart import MIMEMultipart
 | 
					from email.mime.multipart import MIMEMultipart
 | 
				
			||||||
from email.mime.text import MIMEText
 | 
					from email.mime.text import MIMEText
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					os.makedirs("logs", exist_ok=True)
 | 
				
			||||||
 | 
					os.makedirs("exports", exist_ok=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Redis - baza 5
 | 
					# Redis - baza 5
 | 
				
			||||||
redis_client = redis.Redis(host='localhost', port=6379, db=5, decode_responses=True)
 | 
					redis_client = redis.Redis(host='localhost', port=6379, db=5, decode_responses=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					script_path = os.path.abspath(__file__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Tymczasowe domeny
 | 
					# Tymczasowe domeny
 | 
				
			||||||
TEMP_DOMAINS = {
 | 
					DISPOSABLE_DOMAINS_URL = "https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/refs/heads/main/disposable_email_blocklist.conf"
 | 
				
			||||||
    "10minutemail.com", "tempmail.com", "tempmail.net", "tempmail.org",
 | 
					DISPOSABLE_DOMAINS_CACHE_KEY = "disposable_domains:list"
 | 
				
			||||||
    "guerrillamail.com", "mailinator.com", "discard.email", "fakeinbox.com",
 | 
					DISPOSABLE_DOMAINS_TTL = 86400  # 24h
 | 
				
			||||||
    "trashmail.com", "getnada.com", "yopmail.com", "maildrop.cc", "sharklasers.com"
 | 
					
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Logi
 | 
					# Logi
 | 
				
			||||||
logging.basicConfig(
 | 
					logging.basicConfig(
 | 
				
			||||||
    filename='user_cleanup.log',
 | 
					    filename='logs/user_cleanup.log',
 | 
				
			||||||
    level=logging.INFO,
 | 
					    level=logging.INFO,
 | 
				
			||||||
    format='%(asctime)s [%(levelname)s] %(message)s',
 | 
					    format='%(asctime)s [%(levelname)s] %(message)s',
 | 
				
			||||||
    datefmt='%Y-%m-%d %H:%M:%S'
 | 
					    datefmt='%Y-%m-%d %H:%M:%S'
 | 
				
			||||||
@@ -71,16 +76,37 @@ def is_fake_email(email):
 | 
				
			|||||||
    redis_client.set(cache_key, result, ex=259200)
 | 
					    redis_client.set(cache_key, result, ex=259200)
 | 
				
			||||||
    return result == "true"
 | 
					    return result == "true"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def is_temp_email(email):
 | 
					def load_temp_domains():
 | 
				
			||||||
 | 
					    cached = redis_client.get(DISPOSABLE_DOMAINS_CACHE_KEY)
 | 
				
			||||||
 | 
					    if cached:
 | 
				
			||||||
 | 
					        return set(cached.split(","))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        resp = requests.get(DISPOSABLE_DOMAINS_URL, timeout=10)
 | 
				
			||||||
 | 
					        if resp.status_code == 200:
 | 
				
			||||||
 | 
					            domains = {line.strip().lower() for line in resp.text.splitlines() if line.strip() and not line.startswith("#")}
 | 
				
			||||||
 | 
					            redis_client.set(DISPOSABLE_DOMAINS_CACHE_KEY, ",".join(domains), ex=DISPOSABLE_DOMAINS_TTL)
 | 
				
			||||||
 | 
					            return domains
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            print(f"⚠️ Nie udało się pobrać listy disposable domains (kod {resp.status_code})")
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        print(f"⚠️ Błąd pobierania listy disposable domains: {e}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def is_temp_email(email, temp_domains_cache=None):
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        domain = email.split('@')[1].lower()
 | 
					        domain = email.split('@')[1].lower()
 | 
				
			||||||
        return domain in TEMP_DOMAINS
 | 
					        if temp_domains_cache is None:
 | 
				
			||||||
 | 
					            temp_domains_cache = load_temp_domains()
 | 
				
			||||||
 | 
					        return domain in temp_domains_cache
 | 
				
			||||||
    except Exception:
 | 
					    except Exception:
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def export_to_csv(users):
 | 
					def export_to_csv(users):
 | 
				
			||||||
    now = datetime.now().strftime("%Y-%m-%d_%H%M")
 | 
					    now = datetime.now().strftime("%Y-%m-%d_%H%M")
 | 
				
			||||||
    filename = f"user_cleanup_results_{now}.csv"
 | 
					    filename = f"exports/user_cleanup_results_{now}.csv"
 | 
				
			||||||
    with open(filename, mode='w', newline='', encoding='utf-8') as f:
 | 
					    with open(filename, mode='w', newline='', encoding='utf-8') as f:
 | 
				
			||||||
        writer = csv.writer(f)
 | 
					        writer = csv.writer(f)
 | 
				
			||||||
        writer.writerow(["UID", "Login", "E-mail", "Ostatnie logowanie", "Rejestracja", "Punkty", "Nieaktywny", "E-mail OK", "Tymczasowy"])
 | 
					        writer.writerow(["UID", "Login", "E-mail", "Ostatnie logowanie", "Rejestracja", "Punkty", "Nieaktywny", "E-mail OK", "Tymczasowy"])
 | 
				
			||||||
@@ -98,7 +124,7 @@ def export_to_csv(users):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def export_to_excel(users):
 | 
					def export_to_excel(users):
 | 
				
			||||||
    now = datetime.now().strftime("%Y-%m-%d_%H%M")
 | 
					    now = datetime.now().strftime("%Y-%m-%d_%H%M")
 | 
				
			||||||
    filename = f"user_cleanup_results_{now}.xlsx"
 | 
					    filename = f"exports/user_cleanup_results_{now}.xlsx"
 | 
				
			||||||
    workbook = xlsxwriter.Workbook(filename)
 | 
					    workbook = xlsxwriter.Workbook(filename)
 | 
				
			||||||
    sheet = workbook.add_worksheet("Wyniki")
 | 
					    sheet = workbook.add_worksheet("Wyniki")
 | 
				
			||||||
    headers = ["UID", "Login", "E-mail", "Ostatnie logowanie", "Rejestracja", "Punkty", "Nieaktywny", "E-mail OK", "Tymczasowy"]
 | 
					    headers = ["UID", "Login", "E-mail", "Ostatnie logowanie", "Rejestracja", "Punkty", "Nieaktywny", "E-mail OK", "Tymczasowy"]
 | 
				
			||||||
@@ -250,27 +276,49 @@ def main():
 | 
				
			|||||||
        epilog="""
 | 
					        epilog="""
 | 
				
			||||||
            Przykłady użycia:
 | 
					            Przykłady użycia:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Podgląd nieaktywnych użytkowników bez punktów
 | 
					            # 1. Podgląd nieaktywnych użytkowników bez punktów
 | 
				
			||||||
    /root/user_manager/venv/bin/python3 app.py --days-inactive 730 --dry-run
 | 
					            {script_path} --days-inactive 730 --dry-run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Usuń użytkowników z błędnymi e-mailami i nieaktywnych 2+ lata
 | 
					            # 2. Usuń użytkowników z błędnymi e-mailami i nieaktywnych ponad 2 lata
 | 
				
			||||||
    /root/user_manager/venv/bin/python3 app.py --days-inactive 730 --delete
 | 
					            {script_path} --days-inactive 730 --delete
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Uwzględnij starych użytkowników, którzy logowali się ostatnio
 | 
					            # 3. Uwzględnij starych użytkowników (sprzed 2012), którzy logowali się w ciągu ostatnich 3 lat
 | 
				
			||||||
    /root/user_manager/venv/bin/python3 app.py --days-inactive 730 --veteran-year 2012 --recent-login-days 1095
 | 
					            {script_path} --days-inactive 730 --veteran-year 2012 --recent-login-days 1095
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Tylko walidacja adresów e-mail
 | 
					            # 4. Walidacja poprawności adresów e-mail (bez usuwania)
 | 
				
			||||||
    /root/user_manager/venv/bin/python3 app.py --validate
 | 
					            {script_path} --validate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Czyszczenie cache DNS w Redisie
 | 
					            # 5. Czyszczenie cache rekordów MX w Redisie
 | 
				
			||||||
    /root/user_manager/venv/bin/python3 app.py --flush-cache
 | 
					            {script_path} --flush-cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Wyślij maile tylko do użytkowników nieaktywnych od 1 do 5 lat
 | 
					            # 6. Eksportuj dane użytkowników do pliku Excel
 | 
				
			||||||
    /root/user_manager/venv/bin/python3 app.py --send-mails --inactive-since 365-1825
 | 
					            {script_path} --days-inactive 730 --dry-run --export-excel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 7. Wygeneruj raport liczby użytkowników wg domen e-mail
 | 
				
			||||||
 | 
					            {script_path} --days-inactive 730 --report-domains
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 8. Wyświetl tabelę z użytkownikami kwalifikującymi się do usunięcia
 | 
				
			||||||
 | 
					            {script_path} --days-inactive 730 --show-table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 9. Wyślij e-maile do użytkowników nieaktywnych od 1 do 5 lat
 | 
				
			||||||
 | 
					            {script_path} --send-mails --inactive-since 365-1825
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 10. Wyślij testowego maila na podany adres
 | 
				
			||||||
 | 
					            {script_path} --send-test test@example.com
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 11. Usuń tylko użytkowników z nieprawidłowym lub tymczasowym adresem e-mail
 | 
				
			||||||
 | 
					            {script_path} --only-invalid-emails --delete
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 12. Usuń nieaktywnych użytkowników, z ustawioną ścieżką do Drupala
 | 
				
			||||||
 | 
					            {script_path} --days-inactive 730 --delete --drupal-path /var/www/drupal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # 13. Ustaw inną liczbę maili i przerwę między paczkami (np. 50 maili co 30s)
 | 
				
			||||||
 | 
					            {script_path} --send-mails --inactive-since 730-2000 --mails-per-pack 50 --time-per-pack 30
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
        formatter_class=argparse.RawDescriptionHelpFormatter
 | 
					        formatter_class=argparse.RawDescriptionHelpFormatter
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser.add_argument('--host', help='Adres hosta bazy danych (można ustawić w .env jako DB_HOST)')
 | 
					    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('--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('--password', help='Hasło do bazy danych (lub DB_PASSWORD z .env)')
 | 
				
			||||||
@@ -377,6 +425,8 @@ def main():
 | 
				
			|||||||
    skipped_with_points = 0
 | 
					    skipped_with_points = 0
 | 
				
			||||||
    skipped_veterans = 0
 | 
					    skipped_veterans = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    temp_domains_cache = load_temp_domains()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for user in tqdm(users, desc="Analiza"):
 | 
					    for user in tqdm(users, desc="Analiza"):
 | 
				
			||||||
        if (user.get('points') or 0) > 0:
 | 
					        if (user.get('points') or 0) > 0:
 | 
				
			||||||
            skipped_with_points += 1
 | 
					            skipped_with_points += 1
 | 
				
			||||||
@@ -393,7 +443,7 @@ def main():
 | 
				
			|||||||
        is_inactive_by_range = inactive_range and (inactive_range[0] <= days_inactive <= inactive_range[1])
 | 
					        is_inactive_by_range = inactive_range and (inactive_range[0] <= days_inactive <= inactive_range[1])
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        user['inactive'] = is_inactive_by_days or is_inactive_by_range
 | 
					        user['inactive'] = is_inactive_by_days or is_inactive_by_range
 | 
				
			||||||
        user['temp_email'] = is_temp_email(user['mail'])
 | 
					        user['temp_email'] = is_temp_email(user['mail'], temp_domains_cache)
 | 
				
			||||||
        user['email_valid'] = not is_fake_email(user['mail']) and not user['temp_email']
 | 
					        user['email_valid'] = not is_fake_email(user['mail']) and not user['temp_email']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if args.only_invalid_emails:
 | 
					        if args.only_invalid_emails:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										72
									
								
								csv_browser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								csv_browser.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					import os
 | 
				
			||||||
 | 
					from flask import Flask, render_template_string, send_from_directory, request
 | 
				
			||||||
 | 
					import pandas as pd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXPORTS_DIR = "exports"
 | 
				
			||||||
 | 
					PORT = 8899
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app = Flask(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEMPLATE = """
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					    <meta charset="utf-8">
 | 
				
			||||||
 | 
					    <title>CSV Viewer</title>
 | 
				
			||||||
 | 
					    <link rel="stylesheet"
 | 
				
			||||||
 | 
					        href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
 | 
				
			||||||
 | 
					    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
 | 
				
			||||||
 | 
					    <script
 | 
				
			||||||
 | 
					        src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					    <h2>📂 CSV Viewer</h2>
 | 
				
			||||||
 | 
					    {% if files %}
 | 
				
			||||||
 | 
					        <ul>
 | 
				
			||||||
 | 
					        {% for file in files %}
 | 
				
			||||||
 | 
					            <li><a href="/view?file={{ file }}">{{ file }}</a></li>
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					    {% else %}
 | 
				
			||||||
 | 
					        <p>Brak plików w katalogu <code>{{ dir }}</code>.</p>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% if table %}
 | 
				
			||||||
 | 
					        <hr>
 | 
				
			||||||
 | 
					        <h3>Plik: {{ filename }}</h3>
 | 
				
			||||||
 | 
					        {{ table | safe }}
 | 
				
			||||||
 | 
					        <script>
 | 
				
			||||||
 | 
					            $(document).ready(function() {
 | 
				
			||||||
 | 
					                $('#csv-table').DataTable();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        </script>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route("/")
 | 
				
			||||||
 | 
					def index():
 | 
				
			||||||
 | 
					    files = [f for f in os.listdir(EXPORTS_DIR) if f.endswith(".csv")]
 | 
				
			||||||
 | 
					    return render_template_string(TEMPLATE, files=files, table=None, dir=EXPORTS_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route("/view")
 | 
				
			||||||
 | 
					def view_file():
 | 
				
			||||||
 | 
					    filename = request.args.get("file")
 | 
				
			||||||
 | 
					    if not filename or not filename.endswith(".csv"):
 | 
				
			||||||
 | 
					        return "Niepoprawny plik.", 400
 | 
				
			||||||
 | 
					    path = os.path.join(EXPORTS_DIR, filename)
 | 
				
			||||||
 | 
					    if not os.path.exists(path):
 | 
				
			||||||
 | 
					        return "Plik nie istnieje.", 404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        df = pd.read_csv(path)
 | 
				
			||||||
 | 
					        html_table = df.to_html(classes="display", index=False, table_id="csv-table")
 | 
				
			||||||
 | 
					        files = [f for f in os.listdir(EXPORTS_DIR) if f.endswith(".csv")]
 | 
				
			||||||
 | 
					        return render_template_string(TEMPLATE, files=files, table=html_table, filename=filename, dir=EXPORTS_DIR)
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        return f"Błąd odczytu CSV: {e}", 500
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    os.makedirs(EXPORTS_DIR, exist_ok=True)
 | 
				
			||||||
 | 
					    app.run(host="0.0.0.0", port=PORT)
 | 
				
			||||||
@@ -5,3 +5,6 @@ python-dotenv
 | 
				
			|||||||
tqdm
 | 
					tqdm
 | 
				
			||||||
redis
 | 
					redis
 | 
				
			||||||
xlsxwriter
 | 
					xlsxwriter
 | 
				
			||||||
 | 
					requests
 | 
				
			||||||
 | 
					flask
 | 
				
			||||||
 | 
					pandas
 | 
				
			||||||
		Reference in New Issue
	
	Block a user