nowy mail i funkcja
This commit is contained in:
		
							
								
								
									
										59
									
								
								app.py
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								app.py
									
									
									
									
									
								
							@@ -21,6 +21,7 @@ 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
 | 
				
			||||||
 | 
					from datetime import datetime, timedelta 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Redis - baza 5
 | 
					# Redis - baza 5
 | 
				
			||||||
@@ -79,7 +80,6 @@ def is_temp_email(email):
 | 
				
			|||||||
    except Exception:
 | 
					    except Exception:
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
def export_to_csv(users):
 | 
					def export_to_csv(users):
 | 
				
			||||||
    now = datetime.datetime.now().strftime("%Y-%m-%d_%H%M")
 | 
					    now = datetime.datetime.now().strftime("%Y-%m-%d_%H%M")
 | 
				
			||||||
    filename = f"user_cleanup_results_{now}.csv"
 | 
					    filename = f"user_cleanup_results_{now}.csv"
 | 
				
			||||||
@@ -167,7 +167,7 @@ def get_smtp_config():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return config
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def send_email_batch(users, smtp_config, mails_per_pack=100, time_per_pack=60, dry_run=False):
 | 
					def send_email_batch(users, smtp_config, mails_per_pack=100, time_per_pack=60, dry_run=False, inactive_range=None):
 | 
				
			||||||
    import os
 | 
					    import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template_path = "mail_template.html"
 | 
					    template_path = "mail_template.html"
 | 
				
			||||||
@@ -182,6 +182,19 @@ def send_email_batch(users, smtp_config, mails_per_pack=100, time_per_pack=60, d
 | 
				
			|||||||
        print(f"❌ Błąd podczas odczytu szablonu HTML: {e}")
 | 
					        print(f"❌ Błąd podczas odczytu szablonu HTML: {e}")
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Filtrowanie użytkowników według zakresu nieaktywności
 | 
				
			||||||
 | 
					    if inactive_range:
 | 
				
			||||||
 | 
					        min_days, max_days = inactive_range
 | 
				
			||||||
 | 
					        now_ts = int(datetime.datetime.now().timestamp())
 | 
				
			||||||
 | 
					        filtered_users = []
 | 
				
			||||||
 | 
					        for user in users:
 | 
				
			||||||
 | 
					            last_access = user['access'] or 0
 | 
				
			||||||
 | 
					            days_inactive = (now_ts - last_access) / 86400
 | 
				
			||||||
 | 
					            if min_days <= days_inactive <= max_days:
 | 
				
			||||||
 | 
					                filtered_users.append(user)
 | 
				
			||||||
 | 
					        users = filtered_users
 | 
				
			||||||
 | 
					        print(f"📨 Wysyłka maili tylko dla użytkowników nieaktywnych od {min_days} do {max_days} dni ({len(users)} osób)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        smtp = smtplib.SMTP(smtp_config["host"], int(smtp_config["port"]))
 | 
					        smtp = smtplib.SMTP(smtp_config["host"], int(smtp_config["port"]))
 | 
				
			||||||
        smtp.ehlo()
 | 
					        smtp.ehlo()
 | 
				
			||||||
@@ -213,8 +226,11 @@ def send_email_batch(users, smtp_config, mails_per_pack=100, time_per_pack=60, d
 | 
				
			|||||||
                msg['To'] = user['mail']
 | 
					                msg['To'] = user['mail']
 | 
				
			||||||
                msg['Subject'] = "Twoje konto w unitraklub.pl"
 | 
					                msg['Subject'] = "Twoje konto w unitraklub.pl"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                body = template.replace("@user", user['name']).replace(
 | 
					                # Generowanie treści maila z uwzględnieniem ostatniego logowania
 | 
				
			||||||
                    "@rejestracja", datetime.datetime.fromtimestamp(user['created']).strftime('%Y-%m-%d'))
 | 
					                body = template.replace("@user", user['name']) \
 | 
				
			||||||
 | 
					                             .replace("@rejestracja", datetime.datetime.fromtimestamp(user['created']).strftime('%Y-%m-%d')) \
 | 
				
			||||||
 | 
					                             .replace("@ostatnie_logowanie", datetime.datetime.fromtimestamp(user['access']).strftime('%Y-%m-%d') if user['access'] else 'nigdy')
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
                msg.attach(MIMEText(body, 'html'))
 | 
					                msg.attach(MIMEText(body, 'html'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                smtp.send_message(msg)
 | 
					                smtp.send_message(msg)
 | 
				
			||||||
@@ -263,6 +279,9 @@ def main():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    # Czyszczenie cache DNS w Redisie
 | 
					    # Czyszczenie cache DNS w Redisie
 | 
				
			||||||
    /root/user_manager/venv/bin/python3 app.py --flush-cache
 | 
					    /root/user_manager/venv/bin/python3 app.py --flush-cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Wyślij maile tylko do użytkowników nieaktywnych od 1 do 5 lat
 | 
				
			||||||
 | 
					    /root/user_manager/venv/bin/python3 app.py --send-mails --inactive-since 365-1825
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
        formatter_class=argparse.RawDescriptionHelpFormatter
 | 
					        formatter_class=argparse.RawDescriptionHelpFormatter
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@@ -320,9 +339,25 @@ def main():
 | 
				
			|||||||
    parser.add_argument('--only-invalid-emails', action='store_true',
 | 
					    parser.add_argument('--only-invalid-emails', action='store_true',
 | 
				
			||||||
        help='Usuń tylko użytkowników z nieprawidłowymi lub tymczasowymi adresami e-mail (bez sprawdzania aktywności)')
 | 
					        help='Usuń tylko użytkowników z nieprawidłowymi lub tymczasowymi adresami e-mail (bez sprawdzania aktywności)')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # NEW: dodano argument --inactive-since
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        "--inactive-since",
 | 
				
			||||||
 | 
					        type=str,
 | 
				
			||||||
 | 
					        help="Zakres dni nieaktywności w formacie min-max, np. 360-1825 (tylko dla wysyłki maili)"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    args = parser.parse_args()
 | 
					    args = parser.parse_args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # NEW: przetwarzanie parametru --inactive-since
 | 
				
			||||||
 | 
					    inactive_range = None
 | 
				
			||||||
 | 
					    if args.inactive_since:
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            min_days, max_days = map(int, args.inactive_since.split('-'))
 | 
				
			||||||
 | 
					            inactive_range = (min_days, max_days)
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            print(f"Błąd parsowania zakresu dni w --inactive-since: {e}")
 | 
				
			||||||
 | 
					            exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if args.send_test:
 | 
					    if args.send_test:
 | 
				
			||||||
        test_user = {
 | 
					        test_user = {
 | 
				
			||||||
            'name': 'Testowy Użytkownik',
 | 
					            'name': 'Testowy Użytkownik',
 | 
				
			||||||
@@ -334,7 +369,6 @@ def main():
 | 
				
			|||||||
        send_email_batch([test_user], smtp_config, mails_per_pack=1, time_per_pack=0, dry_run=False)
 | 
					        send_email_batch([test_user], smtp_config, mails_per_pack=1, time_per_pack=0, dry_run=False)
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not args.drupal_path:
 | 
					    if not args.drupal_path:
 | 
				
			||||||
        args.drupal_path = os.getenv("DRUPAL_PATH")
 | 
					        args.drupal_path = os.getenv("DRUPAL_PATH")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -406,7 +440,6 @@ def main():
 | 
				
			|||||||
    # Redundant safety filter to exclude any with points
 | 
					    # Redundant safety filter to exclude any with points
 | 
				
			||||||
    final_candidates = [u for u in final_candidates if (u.get('points') or 0) == 0]
 | 
					    final_candidates = [u for u in final_candidates if (u.get('points') or 0) == 0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.report_domains:
 | 
					    if args.report_domains:
 | 
				
			||||||
        domain_report(final_candidates)
 | 
					        domain_report(final_candidates)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -424,19 +457,26 @@ def main():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    export_to_csv(final_candidates)
 | 
					    export_to_csv(final_candidates)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.export_excel:
 | 
					    if args.export_excel:
 | 
				
			||||||
        export_to_excel(final_candidates)
 | 
					        export_to_excel(final_candidates)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if args.send_mails:
 | 
					    if args.send_mails:
 | 
				
			||||||
        send_email_batch(final_candidates, smtp_config, args.mails_per_pack, args.time_per_pack)
 | 
					        send_email_batch(
 | 
				
			||||||
 | 
					            final_candidates, 
 | 
				
			||||||
 | 
					            smtp_config, 
 | 
				
			||||||
 | 
					            args.mails_per_pack, 
 | 
				
			||||||
 | 
					            args.time_per_pack,
 | 
				
			||||||
 | 
					            inactive_range=inactive_range  # NEW: przekazanie zakresu nieaktywności
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print("\n📋 Parametry filtrowania:")
 | 
					    print("\n📋 Parametry filtrowania:")
 | 
				
			||||||
    if args.days_inactive:
 | 
					    if args.days_inactive:
 | 
				
			||||||
        print(f"- Nieaktywni: brak logowania przez ≥ {args.days_inactive} dni (~{days_to_years(args.days_inactive)} lat)")
 | 
					        print(f"- Nieaktywni: brak logowania przez ≥ {args.days_inactive} dni (~{days_to_years(args.days_inactive)} lat)")
 | 
				
			||||||
    print(f"- Weterani: konta zarejestrowane w roku ≤ {args.veteran_year}")
 | 
					    print(f"- Weterani: konta zarejestrowane w roku ≤ {args.veteran_year}")
 | 
				
			||||||
    print(f"- Pominięci weterani: logowanie w ciągu ostatnich ≤ {args.recent_login_days} dni (~{days_to_years(args.recent_login_days)} lat)")
 | 
					    print(f"- Pominięci weterani: logowanie w ciągu ostatnich ≤ {args.recent_login_days} dni (~{days_to_years(args.recent_login_days)} lat)")
 | 
				
			||||||
 | 
					    if args.inactive_since:  # NEW: wyświetlanie zakresu nieaktywności
 | 
				
			||||||
 | 
					        min_days, max_days = map(int, args.inactive_since.split('-'))
 | 
				
			||||||
 | 
					        print(f"- Wysyłka maili tylko dla nieaktywnych od {min_days} do {max_days} dni (~{days_to_years(min_days)}-{days_to_years(max_days)} lat)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print("\n📊 Podsumowanie:")
 | 
					    print("\n📊 Podsumowanie:")
 | 
				
			||||||
    print(f"- Całkowita liczba użytkowników w bazie: {len(users)}")
 | 
					    print(f"- Całkowita liczba użytkowników w bazie: {len(users)}")
 | 
				
			||||||
@@ -459,6 +499,5 @@ def main():
 | 
				
			|||||||
            delete_user_via_php(u['uid'], args.drupal_path)
 | 
					            delete_user_via_php(u['uid'], args.drupal_path)
 | 
				
			||||||
        print(f"✅ Usunięto {len(final_candidates)} użytkowników przez delete_user.php")
 | 
					        print(f"✅ Usunięto {len(final_candidates)} użytkowników przez delete_user.php")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    main()
 | 
					    main()
 | 
				
			||||||
@@ -25,6 +25,11 @@
 | 
				
			|||||||
      font-size: 22px;
 | 
					      font-size: 22px;
 | 
				
			||||||
      color: #222222;
 | 
					      color: #222222;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    h2 {
 | 
				
			||||||
 | 
					      font-size: 18px;
 | 
				
			||||||
 | 
					      color: #222222;
 | 
				
			||||||
 | 
					      margin-top: 25px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    p {
 | 
					    p {
 | 
				
			||||||
      font-size: 16px;
 | 
					      font-size: 16px;
 | 
				
			||||||
      line-height: 1.6;
 | 
					      line-height: 1.6;
 | 
				
			||||||
@@ -42,6 +47,28 @@
 | 
				
			|||||||
      margin: 20px 0;
 | 
					      margin: 20px 0;
 | 
				
			||||||
      font-weight: bold;
 | 
					      font-weight: bold;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    .details {
 | 
				
			||||||
 | 
					      background-color: #f8f9fa;
 | 
				
			||||||
 | 
					      padding: 12px;
 | 
				
			||||||
 | 
					      border-left: 4px solid #3498db;
 | 
				
			||||||
 | 
					      margin: 20px 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .details-item {
 | 
				
			||||||
 | 
					      margin-bottom: 8px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .benefits {
 | 
				
			||||||
 | 
					      background-color: #e8f4fd;
 | 
				
			||||||
 | 
					      padding: 12px;
 | 
				
			||||||
 | 
					      border-left: 4px solid #2980b9;
 | 
				
			||||||
 | 
					      margin: 20px 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .benefits ul {
 | 
				
			||||||
 | 
					      padding-left: 20px;
 | 
				
			||||||
 | 
					      margin: 10px 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .benefits li {
 | 
				
			||||||
 | 
					      margin-bottom: 8px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  </style>
 | 
					  </style>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
@@ -52,14 +79,26 @@
 | 
				
			|||||||
      Kontaktujemy się z Tobą w sprawie Twojego konta w serwisie <strong>unitraklub.pl</strong>.
 | 
					      Kontaktujemy się z Tobą w sprawie Twojego konta w serwisie <strong>unitraklub.pl</strong>.
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <p>
 | 
					    <div class="details">
 | 
				
			||||||
      Zarejestrowałeś/aś konto dnia <strong>@rejestracja</strong>. Niestety, od momentu rejestracji nie wykazało ono żadnej aktywności.
 | 
					      <div class="details-item"><strong>Data rejestracji:</strong> @rejestracja</div>
 | 
				
			||||||
    </p>
 | 
					      <div class="details-item"><strong>Ostatnie logowanie:</strong> @ostatnie_logowanie</div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="highlight">
 | 
					    <div class="highlight">
 | 
				
			||||||
      Jeśli nie zalogujesz się w najbliższym czasie, konto zostanie zablokowane i następnie usunięte.
 | 
					      Jeśli nie zalogujesz się w najbliższym czasie, konto zostanie zablokowane i następnie usunięte.
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <h2>Dlaczego warto zachować obecne konto?</h2>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <div class="benefits">
 | 
				
			||||||
 | 
					      <p>Od pewnego czasu wprowadziliśmy nowe zasady dostępu do cennych zasobów:</p>
 | 
				
			||||||
 | 
					      <ul>
 | 
				
			||||||
 | 
					        <li>Aby korzystać z działu <strong>download</strong>, gdzie znajdziesz schematy, instrukcje serwisowe i inne cenne materiały, wymagany jest minimalny staż w serwisie</li>
 | 
				
			||||||
 | 
					        <li>Twoje obecne konto ma już historię, którą stracisz jeśli zostanie usunięte</li>
 | 
				
			||||||
 | 
					        <li>Długoletnie konta mają pierwszeństwo dostępu do specjalnych zasobów</li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <p>
 | 
					    <p>
 | 
				
			||||||
      Jeśli nadal chcesz korzystać z serwisu, wystarczy się zalogować – to wszystko!
 | 
					      Jeśli nadal chcesz korzystać z serwisu, wystarczy się zalogować – to wszystko!
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user