Dieser Commit ist enthalten in:
Claude Project Manager
2025-07-03 21:11:05 +02:00
Commit 08ed938105
239 geänderte Dateien mit 21554 neuen und 0 gelöschten Zeilen

679
testcases/imap_test.py Normale Datei
Datei anzeigen

@ -0,0 +1,679 @@
#!/usr/bin/env python3
# Path: testcase/imap_test.py
"""
IMAP-Verbindungstest für Social Media Account Generator.
Dieses eigenständige Skript testet die IMAP-Verbindungsdaten aus der Konfigurationsdatei
unter Verwendung der Logik aus email_handler.py.
"""
import imaplib
import email
import json
import os
import sys
import time
import re
from email.header import decode_header
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional, Tuple, Union
# Füge das Hauptverzeichnis zum Pythonpfad hinzu, um Zugriff auf die Konfigurationsdateien zu erhalten
# Der Pfad wird relativ zum Skriptverzeichnis aufgelöst
script_dir = os.path.dirname(os.path.abspath(__file__))
base_dir = os.path.dirname(script_dir) # Gehe eine Ebene hoch zum Hauptverzeichnis
sys.path.insert(0, base_dir)
class EmailTester:
"""
Tester für IMAP-Email-Funktionalität unter Verwendung der Logik aus email_handler.py.
"""
CONFIG_FILE = os.path.join(base_dir, "config", "email_config.json")
def __init__(self):
"""Initialisiert den EmailTester und lädt die Konfiguration."""
self.config = self.load_config()
# Typische Betreffzeilen für Verifizierungs-E-Mails nach Plattform
self.verification_subjects = {
"instagram": [
"Bestätige deine E-Mail-Adresse",
"Bestätigungscode für Instagram",
"Dein Instagram-Code",
"Bestätige deinen Instagram-Account",
"Verify your email address",
"Instagram Verification Code",
"Your Instagram Code",
"Verify your Instagram account",
"Instagram-Bestätigungscode",
"Instagram security code"
],
"facebook": [
"Bestätigungscode für Facebook",
"Facebook-Bestätigungscode",
"Dein Facebook-Code",
"Facebook Verification Code",
"Your Facebook Code"
],
"twitter": [
"Bestätige dein Twitter-Konto",
"Twitter-Bestätigungscode",
"Verify your Twitter account",
"Twitter Verification Code"
],
"tiktok": [
"TikTok-Bestätigungscode",
"Bestätige dein TikTok-Konto",
"TikTok Verification Code",
"Verify your TikTok account"
],
"default": [
"Bestätigungscode",
"Verification Code",
"Account Verification",
"Konto-Bestätigung",
"Security Code",
"Sicherheitscode"
]
}
print(f"EmailTester initialisiert, suche nach Konfiguration in: {self.CONFIG_FILE}")
def load_config(self) -> Dict[str, Any]:
"""
Lädt die E-Mail-Konfiguration aus der Konfigurationsdatei.
Returns:
Dict[str, Any]: Die geladene Konfiguration oder Standardwerte
"""
default_config = {
"imap_server": "imap.ionos.de",
"imap_port": 993,
"imap_user": "info@z5m7q9dk3ah2v1plx6ju.com",
"imap_pass": "cz&ie.O9$!:!tYY@"
}
try:
if os.path.exists(self.CONFIG_FILE):
with open(self.CONFIG_FILE, "r", encoding="utf-8") as f:
config = json.load(f)
print(f"E-Mail-Konfiguration geladen aus: {self.CONFIG_FILE}")
return config
else:
print(f"Konfigurationsdatei nicht gefunden: {self.CONFIG_FILE}")
print("Verwende Standardkonfiguration:")
print(json.dumps(default_config, indent=2))
return default_config
except Exception as e:
print(f"Fehler beim Laden der E-Mail-Konfiguration: {e}")
print("Verwende Standardkonfiguration.")
return default_config
def test_connection(self) -> Dict[str, Any]:
"""
Testet die Verbindung zum IMAP-Server.
Returns:
Dict[str, Any]: Ergebnis des Tests
"""
try:
print(f"Teste Verbindung zu {self.config['imap_server']}:{self.config['imap_port']}")
print(f"Benutzer: {self.config['imap_user']}")
# SSL-Verbindung zum IMAP-Server herstellen
mail = imaplib.IMAP4_SSL(self.config["imap_server"], self.config["imap_port"])
# Anmelden
mail.login(self.config["imap_user"], self.config["imap_pass"])
# Verfügbare Postfächer auflisten
status, mailboxes = mail.list()
if status == 'OK':
print("\nVerfügbare Postfächer:")
mailbox_count = 0
for mailbox in mailboxes:
if isinstance(mailbox, bytes):
try:
# Decode von bytes zu string
decoded_mailbox = mailbox.decode('utf-8')
print(f" - {decoded_mailbox}")
mailbox_count += 1
except:
print(f" - [Nicht decodierbar: {mailbox}]")
else:
print(f" - {mailbox}")
mailbox_count += 1
# INBOX auswählen
mail.select("INBOX")
# Abmelden
mail.logout()
print(f"\nVerbindungstest erfolgreich! {mailbox_count} Postfächer gefunden.")
return {
"success": True,
"server": self.config["imap_server"],
"port": self.config["imap_port"],
"mailbox_count": mailbox_count
}
else:
print(f"Fehler beim Abrufen der Postfächer: {status}")
mail.logout()
return {
"success": False,
"error": f"Fehler beim Abrufen der Postfächer: {status}"
}
except imaplib.IMAP4.error as e:
print(f"IMAP-Fehler: {e}")
return {
"success": False,
"error": f"IMAP-Fehler: {e}"
}
except Exception as e:
print(f"Allgemeiner Fehler: {e}")
return {
"success": False,
"error": f"Allgemeiner Fehler: {e}"
}
def _extract_email_from_addr(self, addr_str: str) -> str:
"""
Extrahiert die E-Mail-Adresse aus einem Adressstring im Format 'Name <email@domain.com>'.
Args:
addr_str: Adressstring
Returns:
str: Die extrahierte E-Mail-Adresse oder der ursprüngliche String
"""
# Regulärer Ausdruck für die Extraktion der E-Mail-Adresse
email_pattern = r'<?([\w\.-]+@[\w\.-]+\.\w+)>?'
match = re.search(email_pattern, addr_str)
if match:
return match.group(1).lower()
return addr_str.lower()
def search_emails(self, search_criteria: str = "ALL", max_emails: int = 5) -> List[Dict[str, Any]]:
"""
Sucht nach E-Mails mit den angegebenen Kriterien.
Args:
search_criteria: IMAP-Suchkriterien
max_emails: Maximale Anzahl der abzurufenden E-Mails
Returns:
List[Dict[str, Any]]: Liste der gefundenen E-Mails
"""
try:
print(f"Suche E-Mails mit Kriterien: {search_criteria}")
print(f"Maximale Anzahl: {max_emails}")
# Verbindung zum IMAP-Server herstellen
mail = imaplib.IMAP4_SSL(self.config["imap_server"], self.config["imap_port"])
# Anmelden
mail.login(self.config["imap_user"], self.config["imap_pass"])
# INBOX auswählen
mail.select("INBOX")
# Nach E-Mails suchen
status, data = mail.search(None, search_criteria)
emails = []
if status == 'OK':
# E-Mail-IDs abrufen
email_ids = data[0].split()
if not email_ids:
print("Keine E-Mails gefunden.")
mail.logout()
return []
# Newest emails first
email_ids = list(reversed(email_ids))
# Begrenze die Anzahl der abzurufenden E-Mails
if max_emails > 0:
email_ids = email_ids[:max_emails]
print(f"Gefunden: {len(email_ids)} E-Mails. Abrufen der Details...")
for i, email_id in enumerate(email_ids):
# E-Mail abrufen
status, data = mail.fetch(email_id, '(RFC822)')
if status == 'OK':
print(f"Verarbeite E-Mail {i+1}/{len(email_ids)}...")
# E-Mail-Inhalt parsen
raw_email = data[0][1]
msg = email.message_from_bytes(raw_email)
# Betreff decodieren
subject = decode_header(msg.get("Subject", ""))[0]
if isinstance(subject[0], bytes):
subject = subject[0].decode(subject[1] or 'utf-8', errors='replace')
else:
subject = subject[0]
# Absender decodieren
from_addr = decode_header(msg.get("From", ""))[0]
if isinstance(from_addr[0], bytes):
from_addr = from_addr[0].decode(from_addr[1] or 'utf-8', errors='replace')
else:
from_addr = from_addr[0]
# Empfänger decodieren
to_addr = decode_header(msg.get("To", ""))[0]
if isinstance(to_addr[0], bytes):
to_addr = to_addr[0].decode(to_addr[1] or 'utf-8', errors='replace')
else:
to_addr = to_addr[0]
# Extrahiere E-Mail-Adresse aus dem To-Feld
to_email = self._extract_email_from_addr(to_addr)
# Datum decodieren
date = msg.get("Date", "")
# E-Mail-Text extrahieren
body = ""
if msg.is_multipart():
for part in msg.walk():
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
if "attachment" not in content_disposition:
if content_type == "text/plain":
try:
# Textinhalt decodieren
charset = part.get_content_charset() or 'utf-8'
body = part.get_payload(decode=True).decode(charset, errors='replace')
break
except:
body = "[Fehler beim Decodieren des Inhalts]"
elif content_type == "text/html" and not body:
try:
# HTML-Inhalt decodieren
charset = part.get_content_charset() or 'utf-8'
body = part.get_payload(decode=True).decode(charset, errors='replace')
except:
body = "[Fehler beim Decodieren des HTML-Inhalts]"
else:
try:
# Einzel-Teil-E-Mail decodieren
charset = msg.get_content_charset() or 'utf-8'
body = msg.get_payload(decode=True).decode(charset, errors='replace')
except:
body = "[Fehler beim Decodieren des Inhalts]"
# E-Mail-Informationen speichern
email_info = {
"id": email_id.decode(),
"subject": subject,
"from": from_addr,
"to": to_addr,
"to_email": to_email,
"date": date,
"body": body
}
# Nach Bestätigungscode im Inhalt suchen
verification_code = self._extract_verification_code(body)
if verification_code:
email_info["verification_code"] = verification_code
emails.append(email_info)
# Abmelden
mail.logout()
print(f"Insgesamt {len(emails)} E-Mails verarbeitet")
return emails
except Exception as e:
print(f"Fehler beim Suchen nach E-Mails: {e}")
return []
def _is_subject_relevant(self, subject: str, platform: str) -> bool:
"""
Prüft, ob der Betreff relevant für eine Verifizierungs-E-Mail der angegebenen Plattform ist.
Args:
subject: Betreff der E-Mail
platform: Plattform (instagram, facebook, twitter, etc.)
Returns:
bool: True, wenn der Betreff relevant ist, False sonst
"""
# Betreffzeilen für die angegebene Plattform und Standard
subject_patterns = self.verification_subjects.get(platform.lower(), [])
subject_patterns += self.verification_subjects["default"]
# Prüfe auf exakte Übereinstimmung (schneller)
for pattern in subject_patterns:
if pattern.lower() in subject.lower():
print(f"Relevanter Betreff gefunden (exakte Übereinstimmung): {subject}")
return True
return False
def _extract_verification_code(self, text: str, platform: str = "instagram") -> Optional[str]:
"""
Extrahiert einen Bestätigungscode aus einem Text.
Args:
text: Zu durchsuchender Text
platform: Plattform (instagram, facebook, twitter, etc.)
Returns:
Optional[str]: Der gefundene Bestätigungscode oder None
"""
# Plattformspezifische Muster für Bestätigungscodes
patterns = {
"instagram": [
r"Dein Code ist (\d{6})",
r"Your code is (\d{6})",
r"Bestätigungscode: (\d{6})",
r"Confirmation code: (\d{6})",
r"(\d{6}) ist dein Instagram-Code",
r"(\d{6}) is your Instagram code",
r"Instagram-Code: (\d{6})",
r"Instagram code: (\d{6})",
r"Instagram: (\d{6})",
r"[^\d](\d{6})[^\d]" # 6-stellige Zahl umgeben von Nicht-Ziffern
],
"facebook": [
r"Dein Code ist (\d{5})",
r"Your code is (\d{5})",
r"Bestätigungscode: (\d{5})",
r"Confirmation code: (\d{5})",
r"Facebook-Code: (\d{5})",
r"Facebook code: (\d{5})",
r"Facebook: (\d{5})",
r"[^\d](\d{5})[^\d]" # 5-stellige Zahl umgeben von Nicht-Ziffern
],
"twitter": [
r"Code: (\d{6})",
r"Verification code: (\d{6})",
r"Twitter-Code: (\d{6})",
r"Twitter code: (\d{6})",
r"Twitter: (\d{6})",
r"[^\d](\d{6})[^\d]" # 6-stellige Zahl umgeben von Nicht-Ziffern
],
"tiktok": [
r"TikTok-Code: (\d{6})",
r"TikTok code: (\d{6})",
r"TikTok: (\d{6})",
r"[^\d](\d{6})[^\d]" # 6-stellige Zahl umgeben von Nicht-Ziffern
],
"default": [
r"Code[:\s]*(\d{4,8})",
r"[Vv]erification [Cc]ode[:\s]*(\d{4,8})",
r"[Bb]estätigungscode[:\s]*(\d{4,8})",
r"(\d{4,8}) is your code",
r"(\d{4,8}) ist dein Code",
r"[^\d](\d{6})[^\d]", # 6-stellige Zahl umgeben von Nicht-Ziffern
r"[^\d](\d{5})[^\d]" # 5-stellige Zahl umgeben von Nicht-Ziffern
]
}
# Plattformspezifische Muster verwenden
platform_patterns = patterns.get(platform.lower(), [])
# Alle Muster dieser Plattform durchsuchen
for pattern in platform_patterns:
match = re.search(pattern, text)
if match:
code = match.group(1)
print(f"Code gefunden mit Muster '{pattern}': {code}")
return code
# Wenn keine plattformspezifischen Muster gefunden wurden, Default-Muster verwenden
for pattern in patterns["default"]:
match = re.search(pattern, text)
if match:
code = match.group(1)
print(f"Code gefunden mit Default-Muster '{pattern}': {code}")
return code
# Generische Suche nach Zahlen (für die jeweilige Plattform typische Länge)
code_length = 6 # Standard
if platform.lower() == "facebook":
code_length = 5
# Suche nach alleinstehenden Zahlen der richtigen Länge
generic_pattern = r"\b(\d{" + str(code_length) + r"})\b"
matches = re.findall(generic_pattern, text)
if matches:
# Nehme die erste gefundene Zahl
code = matches[0]
print(f"Code gefunden mit generischem Muster: {code}")
return code
return None
def find_verification_codes(self, target_email: Optional[str] = None, platform: str = "instagram",
max_emails: int = 10) -> List[Dict[str, Any]]:
"""
Sucht nach Bestätigungscodes in E-Mails.
Args:
target_email: Ziel-E-Mail-Adresse oder None für alle
platform: Plattform (instagram, facebook, twitter, etc.)
max_emails: Maximale Anzahl der zu durchsuchenden E-Mails
Returns:
List[Dict[str, Any]]: Liste der gefundenen E-Mails mit Bestätigungscodes
"""
# Letzter Tag als Suchkriterium
today = datetime.now()
yesterday = today - timedelta(days=1)
date_str = yesterday.strftime("%d-%b-%Y")
search_criteria = f'(SINCE "{date_str}")'
if target_email:
search_criteria = f'(SINCE "{date_str}" TO "{target_email}")'
print(f"Suche nach E-Mails für {target_email} seit {date_str}")
else:
print(f"Suche nach allen E-Mails seit {date_str}")
# Alle E-Mails abrufen
emails = self.search_emails(search_criteria, max_emails=max_emails)
# Relevante E-Mails filtern
verification_emails = []
for email_info in emails:
# Extrahierte E-Mail-Adresse des Empfängers
to_email = email_info.get("to_email", "").lower()
# Wenn eine bestimmte Ziel-E-Mail angegeben ist, prüfe auf Übereinstimmung
if target_email and target_email.lower() != to_email:
# Wenn Domain angegeben wurde, prüfe auf Domain-Übereinstimmung
if '@' in target_email and '@' in to_email:
target_domain = target_email.split('@')[1]
email_domain = to_email.split('@')[1]
if target_domain != email_domain:
continue
else:
continue
# Betreff auf Relevanz prüfen
subject = email_info.get("subject", "")
if self._is_subject_relevant(subject, platform):
verification_emails.append(email_info)
# Alternativ: In der E-Mail nach Bestätigungscodes suchen
elif "verification_code" in email_info or self._extract_verification_code(email_info.get("body", ""), platform):
verification_emails.append(email_info)
print(f"Gefunden: {len(verification_emails)} E-Mails mit Bestätigungscodes oder relevanten Betreffs")
return verification_emails
def display_email(email_info, truncate_body=True):
"""
Zeigt eine E-Mail formatiert an.
Args:
email_info: E-Mail-Informationen
truncate_body: Wenn True, wird der Body gekürzt
"""
print("\n" + "="*80)
print(f"ID: {email_info.get('id', 'N/A')}")
print(f"Von: {email_info.get('from', 'N/A')}")
print(f"An: {email_info.get('to', 'N/A')}")
print(f"Datum: {email_info.get('date', 'N/A')}")
print(f"Betreff: {email_info.get('subject', 'N/A')}")
# Bestätigungscode anzeigen, falls vorhanden
verification_code = email_info.get("verification_code")
if verification_code:
print(f"\n>>> BESTÄTIGUNGSCODE GEFUNDEN: {verification_code} <<<\n")
# Body anzeigen
print("\nInhalt:")
print("-"*80)
body = email_info.get("body", "")
if truncate_body and len(body) > 500:
print(body[:500] + "...\n[Gekürzt - Vollständigen Inhalt mit Option 2 anzeigen]")
else:
print(body)
print("="*80)
def main():
"""Hauptfunktion des Skripts."""
print("="*80)
print("IMAP-Verbindungs- und E-Mail-Test")
print("="*80)
# EmailTester initialisieren
tester = EmailTester()
# Verbindung testen
connection_result = tester.test_connection()
if not connection_result["success"]:
print("\nVerbindungstest fehlgeschlagen!")
print(f"Fehler: {connection_result.get('error', 'Unbekannter Fehler')}")
sys.exit(1)
# Interaktives Menü
while True:
print("\n" + "="*80)
print("MENU")
print("="*80)
print("1. Letzte E-Mails anzeigen")
print("2. Vollständigen Inhalt einer E-Mail anzeigen")
print("3. Nach Bestätigungscodes suchen")
print("4. Bestätigungscodes für eine bestimmte E-Mail-Adresse suchen")
print("5. Verbindungstest erneut durchführen")
print("0. Beenden")
print("="*80)
choice = input("Wählen Sie eine Option: ")
if choice == "1":
# Letzte E-Mails anzeigen
max_emails = int(input("Anzahl der anzuzeigenden E-Mails (Standard: 5): ") or "5")
emails = tester.search_emails(max_emails=max_emails)
if emails:
print(f"\n{len(emails)} E-Mails gefunden:")
for email_info in emails:
display_email(email_info)
else:
print("Keine E-Mails gefunden.")
elif choice == "2":
# Vollständigen Inhalt einer E-Mail anzeigen
email_id = input("Geben Sie die E-Mail-ID ein: ")
if not email_id:
print("Keine ID angegeben.")
continue
# Suche nach der E-Mail mit der angegebenen ID
emails = tester.search_emails(search_criteria=f"(UID {email_id})", max_emails=1)
if emails:
display_email(emails[0], truncate_body=False)
else:
print(f"Keine E-Mail mit ID {email_id} gefunden.")
elif choice == "3":
# Nach Bestätigungscodes in allen E-Mails suchen
platform = input("Plattform (instagram, facebook, twitter, tiktok, default): ") or "instagram"
max_emails = int(input("Maximale Anzahl zu durchsuchender E-Mails (Standard: 10): ") or "10")
verification_emails = tester.find_verification_codes(platform=platform, max_emails=max_emails)
if verification_emails:
print(f"\n{len(verification_emails)} E-Mails mit Bestätigungscodes oder relevanten Betreffs gefunden:")
for email_info in verification_emails:
display_email(email_info)
else:
print("Keine relevanten E-Mails mit Bestätigungscodes gefunden.")
elif choice == "4":
# Nach Bestätigungscodes für eine bestimmte E-Mail-Adresse suchen
email_address = input("E-Mail-Adresse: ")
if not email_address:
print("Keine E-Mail-Adresse angegeben.")
continue
platform = input("Plattform (instagram, facebook, twitter, tiktok, default): ") or "instagram"
max_emails = int(input("Maximale Anzahl zu durchsuchender E-Mails (Standard: 10): ") or "10")
verification_emails = tester.find_verification_codes(
target_email=email_address,
platform=platform,
max_emails=max_emails
)
if verification_emails:
print(f"\n{len(verification_emails)} E-Mails mit Bestätigungscodes für {email_address} gefunden:")
for email_info in verification_emails:
display_email(email_info)
else:
print(f"Keine relevanten E-Mails mit Bestätigungscodes für {email_address} gefunden.")
elif choice == "5":
# Verbindungstest erneut durchführen
connection_result = tester.test_connection()
if not connection_result["success"]:
print("\nVerbindungstest fehlgeschlagen!")
print(f"Fehler: {connection_result.get('error', 'Unbekannter Fehler')}")
elif choice == "0":
# Beenden
print("Programm wird beendet.")
break
else:
print("Ungültige Eingabe. Bitte versuchen Sie es erneut.")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nProgramm durch Benutzer abgebrochen. Auf Wiedersehen!")
except Exception as e:
print(f"\nUnerwarteter Fehler: {e}")