diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f6ec167..44850e9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -18,7 +18,9 @@ "Bash(python -m pip install:*)", "Bash(python -m pytest teststest_generator_tab_factory.py -v)", "Bash(python tests:*)", - "Bash(python tests/test_generator_tab_factory.py)" + "Bash(python tests/test_generator_tab_factory.py)", + "Bash(git push:*)", + "Bash(git remote set-url:*)" ], "deny": [], "defaultMode": "acceptEdits", @@ -26,4 +28,4 @@ "/mnt/a/GiTea/Styleguide" ] } -} \ No newline at end of file +} diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..a8edf1c --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,1038 @@ +# AccountForger - Roadmap + +Dieses Dokument enthält geplante Features und Verbesserungen für AccountForger. + +--- + +## 🎯 Feature 1: Bestätigungscodes-Reiter + +**Priorität:** Hoch +**Status:** Geplant +**Geschätzter Aufwand:** 3-5 Tage + +### Beschreibung +Neuer Reiter "Bestätigungscodes" neben "Plattformen" und "Accounts" für die Verwaltung eingehender Verifizierungscodes. + +### Funktionale Anforderungen + +#### UI/UX Design +- **Chat-ähnliche Oberfläche** mit Liste aller eingehenden Codes +- **Code-Anzeige Format:** + ``` + [Social Media Logo] username_123 + Code: 456789 + 📧 example@domain.com ODER 📱 +49123456789 + ⏱ Löscht sich in 5h 32min + [Kopieren] [Ausblenden] + ``` +- **WICHTIG:** Keine Emojis für Logo-Platzhalter - echte Platform-Icons verwenden +- **Sortierung:** Neueste Codes oben +- **Leerer Zustand:** "Keine Bestätigungscodes vorhanden" + +#### Code-Status & Lebensdauer +- **Automatische Löschung:** Codes werden nach **6 Stunden** automatisch entfernt +- **Countdown-Timer:** Zeigt verbleibende Zeit bis zur Löschung +- **Verwendete Codes:** Grau ausgegraut mit Hinweis "Verwendet" +- **Ausblenden-Funktion:** Nutzer kann Codes manuell ausblenden (kein permanentes Löschen) + +#### Filter & Suche +- **Filter-Optionen:** + - Nach Plattform (Instagram, Facebook, TikTok, etc.) + - Nach Account-Name + - Nach Code-Typ (E-Mail vs SMS) +- **Suchfeld:** Schnellsuche nach Account-Namen +- **Filter kombinierbar:** Mehrere Filter gleichzeitig aktiv + +#### Copy-Funktion +- **Copy-Button:** Kopiert Code mit einem Klick in Zwischenablage +- **Visuelles Feedback:** "Kopiert!" kurz anzeigen nach Klick + +#### Code-Abfrage & Aktualisierung +- **Automatische Abfrage erfolgt bei:** + 1. **Account-Erstellung gestartet** → Polling alle 10-15 Sekunden + 2. **Login-Versuch fehlgeschlagen** (Code erwartet) → Sofort prüfen + alle 5 Sekunden + 3. **Manueller Refresh-Button** → Auf Knopfdruck + 4. **Öffnen des Bestätigungscodes-Reiters** → Einmalig beim Laden + +- **Intelligentes Polling:** + - Polling stoppt wenn kein aktiver Prozess läuft (Server-Schonung) + - Status-Indikator zeigt Aktualisierungsstatus: + - "Prüfe auf neue Codes..." (während Abfrage) + - "Aktualisiert vor X Sekunden" (nach erfolgreicher Abfrage) + +#### Datenquellen +- **E-Mail-Codes:** Abfrage über Server-API (perspektivisch, siehe Feature 3) +- **SMS-Codes:** Abfrage über Server-API (perspektivisch) +- Codes erscheinen automatisch wenn sie vom Server abgerufen werden + +### Technische Umsetzung + +#### Betroffene Module +- `views/components/` - Neuer Tab für Bestätigungscodes +- `views/tabs/` - Tab-Integration in Hauptfenster +- `controllers/` - Controller für Code-Verwaltung +- `database/` - Repository für Code-Speicherung +- `licensing/api_client.py` - API-Calls für Code-Abruf (perspektivisch) + +#### Datenbank-Schema +```sql +CREATE TABLE verification_codes ( + id INTEGER PRIMARY KEY, + account_name TEXT NOT NULL, + platform TEXT NOT NULL, + code TEXT NOT NULL, + source_type TEXT NOT NULL, -- 'email' oder 'sms' + source_value TEXT NOT NULL, -- E-Mail-Adresse oder Telefonnummer + received_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + expires_at TIMESTAMP NOT NULL, + used BOOLEAN DEFAULT 0, + hidden BOOLEAN DEFAULT 0, + auto_used BOOLEAN DEFAULT 0 +); +``` + +#### API-Endpunkte (perspektivisch) +- `GET /api/codes/poll` - Neue Codes abrufen +- `POST /api/codes/mark-used` - Code als verwendet markieren + +### Abhängigkeiten +- Feature 3 (Server-seitige E-Mail/SMS-Abfrage) muss implementiert sein für vollständige Funktionalität +- Bis dahin: Lokale E-Mail/SMS-Abfrage als Übergangslösung + +--- + +## 📦 Feature 2: Profil-Export + +**Priorität:** Mittel +**Status:** Geplant +**Geschätzter Aufwand:** 2-3 Tage + +### Beschreibung +Export-Funktion für einzelne Account-Profile zur Weitergabe an andere Nutzer oder als Backup. + +### Funktionale Anforderungen + +#### Export-Umfang +**Exportierte Daten:** +- ✓ Benutzername +- ✓ Passwort +- ✓ E-Mail-Adresse +- ✓ Telefonnummer +- ✓ Plattform +- ✓ Vor-/Nachname +- ✓ Geburtsdatum +- ✓ Erstellungsdatum + +**NICHT exportiert:** +- ✗ Browser-Cookies +- ✗ Session Storage +- ✗ Browser-Fingerprint +- ✗ Proxy-Einstellungen + +#### Export-Formate + +##### 1. CSV-Format +```csv +Username,Passwort,E-Mail,Telefon,Plattform,Vorname,Nachname,Geburtsdatum,Erstellt_am +waster_ha_948,P@ssw0rd123,waster@domain.com,+49123456789,Instagram,Max,Mustermann,15.03.1995,01.11.2025 +``` +- **Trennzeichen:** Komma (,) +- **Encoding:** UTF-8 +- **Kopfzeile:** Ja (erste Zeile) +- **Auch bei Einzelexport:** Nur eine Datenzeile + +##### 2. TXT-Format +``` +Username: waster_ha_948 +Passwort: P@ssw0rd123 +E-Mail: waster@domain.com +Telefon: +49123456789 +Plattform: Instagram +Name: Max Mustermann +Geburtsdatum: 15.03.1995 +Erstellt am: 01.11.2025 +``` +- **Struktur:** Key-Value Paare +- **Encoding:** UTF-8 + +##### 3. PDF-Format +``` +┌────────────────────────────────────────┐ +│ [IntelSight Logo] │ +│ │ +│ Account-Profil: waster_ha_948 │ +│ Plattform: [Instagram Logo] Instagram │ +│ │ +│ LOGIN-DATEN │ +│ • Benutzername: waster_ha_948 │ +│ • Passwort: P@ssw0rd123 │ +│ • E-Mail: waster@domain.com │ +│ • Telefon: +49123456789 │ +│ │ +│ PROFIL-INFORMATIONEN │ +│ • Name: Max Mustermann │ +│ • Geburtsdatum: 15.03.1995 │ +│ • Erstellt am: 01.11.2025 │ +│ │ +│ Exportiert am: 09.11.2025 15:30 │ +└────────────────────────────────────────┘ +``` +- **Layout:** Schöne Übersicht mit Logos +- **IntelSight-Logo:** Oben links +- **Platform-Logo:** Bei Plattform-Angabe +- **Fallback:** Text-Only PDF wenn Logos nicht verfügbar + +#### Mehrfach-Auswahl +- **CSV + TXT + PDF** gleichzeitig auswählbar +- Alle ausgewählten Formate werden in einem Vorgang exportiert + +#### Dateinamen-Konvention +``` +{username}_{plattform}_{exportdatum}.{format} + +Beispiele: +waster_ha_948_instagram_2025-11-09_15-30.csv +waster_ha_948_instagram_2025-11-09_15-30.txt +waster_ha_948_instagram_2025-11-09_15-30.pdf +``` +- **Datumsformat:** `YYYY-MM-DD_HH-mm` +- **Kleinschreibung:** Plattform-Name in Kleinbuchstaben + +#### Passwortschutz + +##### Automatische Passwort-Generierung +- **Aktivierung:** Optional per Checkbox im Export-Dialog +- **Länge:** 10 Zeichen +- **Zeichen:** Alphanumerisch + Sonderzeichen +- **Beispiel:** `Xy7#mK9@pL` + +##### Passwort-Anzeige +Nach erfolgreichem Export wird Passwort angezeigt: +``` +┌─ Export erfolgreich ──────────────────┐ +│ │ +│ ✓ Profil exportiert! │ +│ │ +│ Datei: │ +│ waster_ha_948_instagram_2025-...zip │ +│ │ +│ 🔒 Passwort: Xy7#mK9@pL │ +│ [Kopieren] │ +│ │ +│ ⚠ Bitte Passwort speichern! │ +│ │ +│ [Ordner öffnen] [OK] │ +└────────────────────────────────────────┘ +``` +- **Kopieren-Button:** Kopiert Passwort in Zwischenablage +- **Warnung:** Nutzer wird aufgefordert, Passwort zu speichern + +##### Verschlüsselung +**Mit Passwortschutz:** +- Alle ausgewählten Formate werden in **eine ZIP-Datei** gepackt +- ZIP-Datei wird mit generiertem Passwort verschlüsselt +- Dateiname: `{username}_{plattform}_{exportdatum}.zip` + - Enthält: CSV, TXT, PDF (je nach Auswahl) + +**Ohne Passwortschutz:** +- Separate Dateien für jedes Format +- Keine ZIP-Datei + +#### Export-Dialog + +``` +┌─ Profil exportieren ──────────────┐ +│ │ +│ Format (mehrfach auswählbar): │ +│ ☑ CSV │ +│ ☑ TXT │ +│ ☐ PDF │ +│ │ +│ ☑ Mit Passwort schützen │ +│ (Wird automatisch generiert) │ +│ │ +│ [Abbrechen] [Exportieren] │ +└────────────────────────────────────┘ +``` + +- **Mindestens ein Format** muss ausgewählt sein +- Bei Passwortschutz: Keine manuelle Passworteingabe +- Nach Klick auf "Exportieren": Save-Dialog öffnet sich + +#### Speicherort +- **Nutzer wählt** Speicherort per Standard-Datei-Dialog +- **Kein Standard-Ordner** vordefiniert +- Letzte Auswahl wird **nicht** gespeichert + +#### UI-Integration +- **Bestehender Button:** "Exportieren" im Accounts-Tab wird erweitert +- **Einzelexport:** Nur der aktuell ausgewählte Account +- **Kein Batch-Export:** Mehrfach-Export vorerst nicht implementiert + +#### Erfolgs-Feedback + +``` +┌─ Export erfolgreich ──────────────────┐ +│ │ +│ ✓ Profil exportiert! │ +│ │ +│ Dateien: │ +│ • waster_ha_948_instagram_2025-...csv │ +│ • waster_ha_948_instagram_2025-...txt │ +│ │ +│ [MIT PASSWORT: Passwort-Anzeige] │ +│ │ +│ Gespeichert in: C:\Users\...\ │ +│ │ +│ [Ordner öffnen] [OK] │ +└────────────────────────────────────────┘ +``` + +- **Dateiliste:** Alle exportierten Dateien anzeigen +- **Ordner öffnen:** Button öffnet Zielordner im Explorer/Finder +- **Passwort:** Nur wenn Passwortschutz aktiviert wurde + +#### Fehlerbehandlung +- **Export fehlgeschlagen:** Fehlermeldung mit Grund + - "Festplatte voll" + - "Keine Schreibrechte" + - "Datei bereits vorhanden und schreibgeschützt" +- **Logo nicht gefunden:** PDF-Export mit Text-Only Fallback + +### Technische Umsetzung + +#### Betroffene Module +- `views/tabs/accounts_tab.py` - Erweiterung Export-Button +- `views/dialogs/` - Neue Export-Dialogs +- `controllers/` - Export-Controller +- `utils/` - Export-Service für Format-Generierung +- `resources/icons/` - Platform-Logos für PDF + +#### Abhängigkeiten +- **Python-Libraries:** + - `csv` - CSV-Export (Standard-Library) + - `reportlab` oder `fpdf` - PDF-Generierung + - `zipfile` - ZIP mit Passwortschutz (Standard-Library) + - `secrets` - Sichere Passwort-Generierung (Standard-Library) + +#### Export-Service Struktur +```python +class ProfileExportService: + def export_to_csv(account_data) -> bytes + def export_to_txt(account_data) -> bytes + def export_to_pdf(account_data) -> bytes + def generate_password() -> str + def create_protected_zip(files, password) -> bytes +``` + +### Ausschlüsse +- **Kein Re-Import:** Import-Funktion wird NICHT implementiert +- **Kein Batch-Export:** Nur Einzelexport +- **Keine Verschlüsselung ohne Passwort:** Ungeschützte Dateien sind Plain-Text + +--- + +## 📋 Weitere Features + +### Feature 3: Server-seitige E-Mail/SMS-Abfrage +**Status:** Geplant +*Details folgen nach Abstimmung* + +--- + +## 🔒 Feature 4: Privater Account erstellen + +**Priorität:** Mittel +**Status:** Geplant +**Geschätzter Aufwand:** 3-4 Tage + +### Beschreibung +Checkbox in der Account-Erstellungsmaske zum automatischen Setzen des Account-Privatsphäre-Modus auf "privat" während der Registrierung. + +### Funktionale Anforderungen + +#### Unterstützte Plattformen +- **Facebook** - Privacy Settings auf "Friends" +- **Instagram** - Private Account aktivieren +- **TikTok** - Private Account aktivieren +- **X/Twitter** - Protect your posts aktivieren + +**NICHT unterstützt:** +- Gmail (keine Privatsphäre-Einstellung in diesem Sinne) +- VK, OK.ru (vorerst) + +#### UI-Integration + +**Checkbox in Account-Erstellungsmaske:** +``` +┌─ Account erstellen (Instagram) ────┐ +│ │ +│ Vorname: [_______________] │ +│ Nachname: [_______________] │ +│ Alter: [___] │ +│ │ +│ ☐ Privaten Account erstellen │ +│ │ +│ Registrierungsmethode: │ +│ ○ E-Mail ○ Telefon │ +│ │ +│ [Abbrechen] [Erstellen] │ +└─────────────────────────────────────┘ +``` + +- **Position:** Unterhalb der Basis-Daten (Name, Alter), oberhalb Registrierungsmethode +- **Standard:** Deaktiviert (nicht angehakt) +- **Sichtbarkeit:** Nur bei unterstützten Plattformen anzeigen +- **Label:** "Privaten Account erstellen" + +#### Ablauf + +**Wenn Checkbox aktiviert:** +``` +1. Normale Account-Erstellung (wie bisher) + - Registrierung + - Profil-Daten eingeben + - Verifizierung abschließen + +2. Account erfolgreich erstellt + ↓ +3. Navigiere zu Privacy-Settings + ↓ +4. Setze Account auf "privat" + - Instagram: Settings → Privacy → Private Account (Toggle ON) + - Facebook: Settings → Privacy → Who can see posts → Friends + - TikTok: Settings → Privacy → Private Account (Toggle ON) + - X: Settings → Privacy and Safety → Protect your posts (Toggle ON) + ↓ +5. Zurück zum Hauptbildschirm +``` + +**Timing:** Als **Teil des Registrierungs-Flows**, direkt nach erfolgreicher Verifizierung, bevor "Account erstellt"-Dialog erscheint. + +#### Erfolgs-/Fehler-Feedback + +**Bei Erfolg:** +``` +┌─ Account erstellt ────────────────┐ +│ │ +│ ✓ Account erstellt: waster_ha_948 │ +│ ✓ Account auf privat gesetzt │ +│ │ +│ [OK] │ +└────────────────────────────────────┘ +``` + +**Bei Fehler:** +``` +┌─ Account erstellt ────────────────┐ +│ │ +│ ✓ Account erstellt: waster_ha_948 │ +│ │ +│ ⚠ Account konnte nicht auf privat │ +│ gesetzt werden │ +│ │ +│ Fehler: Timeout beim Laden der │ +│ Privacy-Settings │ +│ │ +│ Bitte setzen Sie den Account │ +│ manuell auf privat. │ +│ │ +│ [OK] │ +└────────────────────────────────────┘ +``` + +- **Kein Retry:** Keine "Erneut versuchen" Option +- **Account behalten:** Account wird auch bei Fehler gespeichert +- **Nutzer-Aktion:** Hinweis dass manuelles Setzen erforderlich ist + +#### Keine Verifizierung +- **Keine Prüfung** ob Account tatsächlich privat ist +- Software versucht nur, Privacy-Setting zu setzen +- Bei Erfolg (kein Fehler) → Annehmen dass es funktioniert hat + +### Technische Umsetzung + +#### Betroffene Module +- `views/tabs/generator_tab_factory.py` - Checkbox hinzufügen +- `views/tabs/facebook_generator_tab.py` - Facebook-spezifische UI +- `views/tabs/instagram_generator_tab.py` - Instagram-spezifische UI (falls existent) +- `social_networks/facebook/facebook_workflow.py` - Privacy-Setting Flow +- `social_networks/instagram/instagram_workflow.py` - Privacy-Setting Flow +- `social_networks/tiktok/tiktok_workflow.py` - Privacy-Setting Flow +- `social_networks/x/x_workflow.py` - Privacy-Setting Flow + +#### Implementierung pro Plattform + +**Neue Methoden in Workflow-Klassen:** +```python +class FacebookWorkflow: + def set_privacy_to_private(self) -> bool: + """ + Setzt Facebook-Account auf privat (Friends only). + + Returns: + True bei Erfolg, False bei Fehler + """ + try: + # 1. Navigiere zu Settings + # 2. Öffne Privacy-Bereich + # 3. Setze "Who can see posts" auf "Friends" + # 4. Speichern + return True + except Exception as e: + logger.error(f"Privacy-Setting fehlgeschlagen: {e}") + return False +``` + +**Analoger Code für Instagram, TikTok, X** + +#### Workflow-Integration + +**In Account-Creation Workflow:** +```python +# Nach erfolgreicher Verifizierung +if registration_successful: + # Account in DB speichern + save_account_to_db(account_data) + + # Wenn "Privat"-Checkbox aktiviert war + if create_private_account: + try: + privacy_success = workflow.set_privacy_to_private() + + if privacy_success: + return { + "success": True, + "message": "Account erstellt und auf privat gesetzt", + "privacy_set": True + } + else: + return { + "success": True, + "message": "Account erstellt, aber Privacy-Setting fehlgeschlagen", + "privacy_set": False, + "privacy_error": "Konnte nicht auf privat gesetzt werden" + } + except Exception as e: + return { + "success": True, + "message": "Account erstellt", + "privacy_set": False, + "privacy_error": str(e) + } + else: + return { + "success": True, + "message": "Account erstellt", + "privacy_set": None # Nicht versucht + } +``` + +#### Selektoren (pro Plattform neu) + +**Neue Selector-Definitionen benötigt:** +- `facebook_selectors.py` - Privacy Settings Selektoren +- `instagram_selectors.py` - Privacy Settings Selektoren +- `tiktok_selectors.py` - Privacy Settings Selektoren +- `x_selectors.py` - Privacy Settings Selektoren + +### Abhängigkeiten +- **Playwright/Browser-Automatisierung** muss Privacy-Settings-Seiten navigieren können +- **Plattform-spezifische Selektoren** müssen recherchiert und getestet werden +- **Zeitverzögerung:** Rechne mit +10-20 Sekunden pro Account-Erstellung + +### Risiken & Bekannte Probleme +- **UI-Änderungen:** Wenn Plattformen Privacy-Settings-UI ändern, bricht Feature +- **Rate-Limiting:** Zusätzliche Aktionen könnten Rate-Limits schneller erreichen +- **A/B-Testing:** Manche Nutzer sehen andere UI-Varianten +- **Keine Garantie:** Auch bei "Erfolg" könnte Setting nicht gesetzt sein (keine Verifizierung) + +### Ausschlüsse +- **Keine Verifizierung:** Wird nicht geprüft ob Account wirklich privat ist +- **Kein Retry:** Bei Fehler keine automatische Wiederholung +- **Keine Datenbank-Speicherung:** `is_private` Flag wird NICHT in DB gespeichert +- **Keine nachträgliche Änderung:** Funktion nur während Erstellung, nicht nachträglich änderbar + +--- + +## 🛡️ Feature 5: Software Missbrauch Schutz + +**Priorität:** Hoch +**Status:** Geplant +**Geschätzter Aufwand:** 2-3 Tage + +### Beschreibung +Schutzmaßnahmen gegen versehentlichen oder absichtlichen Missbrauch der Software durch parallele Prozesse, zu viele Browser-Instanzen oder wiederholte Fehlversuche. + +### Funktionale Anforderungen + +#### 1. Process Lock Manager + +**Zentrale Steuerung für alle Prozesse:** +- Nur **ein Vorgang gleichzeitig** erlaubt +- Gilt für: Account-Erstellung, Login, Export (alle Prozess-Typen) +- Plattform-übergreifend: Keine Instagram-Erstellung während Facebook-Login + +**Verhalten:** + +``` +Situation: Instagram-Account wird erstellt + ↓ +Nutzer versucht Facebook-Account zu erstellen + ↓ +⚠️ Warnung: "Prozess läuft bereits" + ↓ +"Bitte warten Sie bis 'Account-Erstellung (Instagram)' + abgeschlossen ist." + ↓ +Buttons ausgegraut + Tooltip +``` + +**UI-Feedback:** +- Alle Prozess-Buttons werden **deaktiviert** (ausgegraut) +- Tooltip zeigt: "Warten Sie bis aktueller Vorgang abgeschlossen ist" +- Warnmeldung bei Klick-Versuch auf deaktivierten Button + +**Crash-Sicherheit:** +- Bei Programm-Neustart: Lock wird **automatisch zurückgesetzt** +- Keine persistente Speicherung des Lock-Status +- Lock nur im Memory während Laufzeit + +#### 2. Browser-Instanz Schutz + +**Nur eine Playwright-Instanz gleichzeitig:** +- Verhindert mehrere Browser-Fenster parallel +- Ressourcen-Schonung (RAM, CPU) +- Vermeidet Konflikte zwischen Browser-Instanzen + +**Implementierung:** +```python +class PlaywrightManager: + _active_browser_count = 0 + _max_browsers = 1 + + def launch_browser(self): + if self._active_browser_count >= self._max_browsers: + raise Exception( + "Browser bereits aktiv. " + "Beenden Sie den aktuellen Prozess." + ) + + self._active_browser_count += 1 + # ... Browser starten + + def close_browser(self): + self._active_browser_count -= 1 +``` + +**Fehlerbehandlung:** +- Bei Absturz: Browser-Counter wird beim Neustart zurückgesetzt +- Bei forciertem Schließen: Counter wird dekrementiert + +#### 3. Fehler-Tracking mit Zwangspause + +**Automatische Pause nach wiederholten Fehlschlägen:** +- Nach **3 fehlgeschlagenen** Account-Erstellungen **hintereinander** +- **Zwangspause von 1 Stunde** (nicht überspringbar) +- Verhindert Rate-Limiting und IP-Sperren + +**Fehler-Zählung:** + +``` +Versuch 1: Instagram Account → Fehler ❌ (Zähler: 1) +Versuch 2: Facebook Account → Fehler ❌ (Zähler: 2) +Versuch 3: TikTok Account → Fehler ❌ (Zähler: 3) + ↓ +🚫 ZWANGSPAUSE AKTIVIERT (1 Stunde) +``` + +**Reset-Bedingungen:** +- Bei **Erfolg**: Fehler-Zähler wird auf 0 zurückgesetzt +- Nach **Pause-Ende**: Fehler-Zähler wird auf 0 zurückgesetzt +- Bei **Programm-Neustart**: Fehler-Zähler wird NICHT zurückgesetzt (persistiert) + +**UI während Zwangspause:** + +``` +┌─ Automatische Pause aktiv ────────┐ +│ │ +│ ⏸️ Zwangspause wegen Fehlerrate │ +│ │ +│ Nach 3 fehlgeschlagenen Versuchen │ +│ ist eine Pause erforderlich. │ +│ │ +│ ⏰ Verbleibende Zeit: 47 Min │ +│ │ +│ Empfehlung: │ +│ • Proxy-Einstellungen prüfen │ +│ • Internetverbindung prüfen │ +│ • Plattform-Status überprüfen │ +│ │ +│ [Schließen] │ +└────────────────────────────────────┘ +``` + +- **Countdown-Timer** zeigt verbleibende Zeit +- Alle Prozess-Buttons **deaktiviert** +- **Nicht überspringbar** - kein "Pause beenden" Button +- Persistiert über Programm-Neustarts (in Datei gespeichert) + +**Persistierung:** +```json +// config/.error_tracking +{ + "consecutive_failures": 3, + "pause_until": "2025-11-09T16:30:00", + "last_failure_platform": "Instagram", + "last_failure_reason": "Timeout bei Verifizierung" +} +``` + +### Technische Umsetzung + +#### Process Lock Manager Klasse + +```python +# utils/process_lock_manager.py + +import time +from typing import Optional +from datetime import datetime + +class ProcessLockManager: + """ + Zentrale Verwaltung für Prozess-Locks und Fehler-Tracking. + Singleton-Pattern für globalen Zugriff. + """ + + _instance = None + _lock_active = False + _current_process = None + _current_platform = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super().__new__(cls) + return cls._instance + + @classmethod + def start_process(cls, process_type: str, platform: str) -> bool: + """ + Versucht einen Prozess zu starten. + + Args: + process_type: Art des Prozesses ("Account-Erstellung", "Login", etc.) + platform: Plattform (Instagram, Facebook, etc.) + + Returns: + True wenn gestartet, False wenn blockiert + """ + if cls._lock_active: + return False + + cls._lock_active = True + cls._current_process = process_type + cls._current_platform = platform + return True + + @classmethod + def end_process(cls, success: bool = True): + """ + Beendet den aktuellen Prozess. + + Args: + success: Ob Prozess erfolgreich war + """ + cls._lock_active = False + cls._current_process = None + cls._current_platform = None + + # Fehler-Tracking aktualisieren + if success: + ErrorTracker.reset_failures() + else: + ErrorTracker.report_failure(cls._current_platform) + + @classmethod + def is_locked(cls) -> bool: + """Gibt zurück ob aktuell ein Prozess läuft""" + return cls._lock_active + + @classmethod + def get_current_process_info(cls) -> Optional[dict]: + """Gibt Info über aktuellen Prozess zurück""" + if not cls._lock_active: + return None + + return { + "type": cls._current_process, + "platform": cls._current_platform + } + + @classmethod + def force_unlock(cls, reason: str = "Manuell"): + """ + Entsperrt zwangsweise (nur bei Absturz). + + Args: + reason: Grund für Force-Unlock + """ + logger.warning(f"Process Lock wurde entsperrt: {reason}") + cls._lock_active = False + cls._current_process = None + cls._current_platform = None + + @classmethod + def reset_on_startup(cls): + """Wird beim Programmstart aufgerufen""" + cls.force_unlock("Programm-Neustart") +``` + +#### Error Tracker Klasse + +```python +# utils/error_tracker.py + +import json +import os +from datetime import datetime, timedelta +from typing import Optional + +class ErrorTracker: + """Tracking von fehlgeschlagenen Prozessen mit Zwangspause.""" + + CONFIG_FILE = os.path.join("config", ".error_tracking") + MAX_FAILURES = 3 + PAUSE_DURATION_HOURS = 1 + + _consecutive_failures = 0 + _pause_until = None + + @classmethod + def load_state(cls): + """Lädt gespeicherten State beim Start""" + if not os.path.exists(cls.CONFIG_FILE): + return + + try: + with open(cls.CONFIG_FILE, 'r') as f: + data = json.load(f) + + cls._consecutive_failures = data.get('consecutive_failures', 0) + + pause_until_str = data.get('pause_until') + if pause_until_str: + cls._pause_until = datetime.fromisoformat(pause_until_str) + + # Prüfe ob Pause abgelaufen + if datetime.now() > cls._pause_until: + cls._pause_until = None + cls._consecutive_failures = 0 + cls._save_state() + + except Exception as e: + logger.error(f"Fehler beim Laden von Error Tracking: {e}") + + @classmethod + def _save_state(cls): + """Speichert aktuellen State""" + try: + os.makedirs(os.path.dirname(cls.CONFIG_FILE), exist_ok=True) + + data = { + 'consecutive_failures': cls._consecutive_failures, + 'pause_until': cls._pause_until.isoformat() if cls._pause_until else None, + 'last_update': datetime.now().isoformat() + } + + with open(cls.CONFIG_FILE, 'w') as f: + json.dump(data, f, indent=2) + + except Exception as e: + logger.error(f"Fehler beim Speichern von Error Tracking: {e}") + + @classmethod + def report_failure(cls, platform: str): + """ + Meldet einen Fehlschlag. + + Args: + platform: Plattform auf der Fehler auftrat + """ + cls._consecutive_failures += 1 + logger.warning(f"Fehlschlag #{cls._consecutive_failures} auf {platform}") + + if cls._consecutive_failures >= cls.MAX_FAILURES: + # Aktiviere Zwangspause + cls._pause_until = datetime.now() + timedelta(hours=cls.PAUSE_DURATION_HOURS) + logger.error( + f"Zwangspause aktiviert bis {cls._pause_until.strftime('%H:%M')} " + f"nach {cls.MAX_FAILURES} Fehlschlägen" + ) + + cls._save_state() + + @classmethod + def reset_failures(cls): + """Setzt Fehler-Zähler bei Erfolg zurück""" + if cls._consecutive_failures > 0: + logger.info("Fehler-Zähler zurückgesetzt nach Erfolg") + cls._consecutive_failures = 0 + cls._save_state() + + @classmethod + def is_paused(cls) -> bool: + """Gibt zurück ob aktuell Zwangspause aktiv ist""" + if cls._pause_until is None: + return False + + if datetime.now() > cls._pause_until: + # Pause ist abgelaufen + cls._pause_until = None + cls._consecutive_failures = 0 + cls._save_state() + return False + + return True + + @classmethod + def get_pause_remaining_seconds(cls) -> Optional[int]: + """Gibt verbleibende Sekunden der Pause zurück""" + if not cls.is_paused(): + return None + + remaining = (cls._pause_until - datetime.now()).total_seconds() + return max(0, int(remaining)) + + @classmethod + def get_pause_info(cls) -> Optional[dict]: + """Gibt Info über aktive Pause zurück""" + if not cls.is_paused(): + return None + + remaining_seconds = cls.get_pause_remaining_seconds() + remaining_minutes = remaining_seconds // 60 + + return { + 'active': True, + 'remaining_seconds': remaining_seconds, + 'remaining_minutes': remaining_minutes, + 'pause_until': cls._pause_until.strftime('%H:%M'), + 'consecutive_failures': cls._consecutive_failures + } +``` + +#### Integration in bestehenden Code + +**In main.py beim Start:** +```python +# Beim Programmstart +ProcessLockManager.reset_on_startup() +ErrorTracker.load_state() + +# Prüfe ob Pause aktiv +if ErrorTracker.is_paused(): + pause_info = ErrorTracker.get_pause_info() + show_pause_dialog(pause_info) +``` + +**In allen Controller-Klassen:** +```python +# facebook_controller.py, instagram_controller.py, etc. + +def create_account(self, account_data): + # 1. Prüfe Zwangspause + if ErrorTracker.is_paused(): + pause_info = ErrorTracker.get_pause_info() + show_pause_warning(pause_info) + return + + # 2. Prüfe Process Lock + if not ProcessLockManager.start_process("Account-Erstellung", "Instagram"): + current = ProcessLockManager.get_current_process_info() + show_warning( + "Prozess läuft bereits", + f"Bitte warten Sie bis '{current['type']} ({current['platform']})' " + f"abgeschlossen ist." + ) + return + + try: + # 3. Normaler Prozess + self.show_modal() + result = self._perform_account_creation(account_data) + + # 4. Lock freigeben mit Erfolgs-Status + ProcessLockManager.end_process(success=result['success']) + + return result + + except Exception as e: + # Bei Fehler: Lock freigeben mit Fehler-Status + ProcessLockManager.end_process(success=False) + raise +``` + +**In Generator Tabs (UI):** +```python +# views/tabs/generator_tab.py + +def update_ui_state(self): + """Aktualisiert Button-Status basierend auf Locks""" + + # Prüfe Zwangspause + is_paused = ErrorTracker.is_paused() + is_locked = ProcessLockManager.is_locked() + + # Deaktiviere Buttons wenn Pause oder Lock aktiv + self.create_button.setEnabled(not is_paused and not is_locked) + + # Tooltip setzen + if is_paused: + pause_info = ErrorTracker.get_pause_info() + tooltip = f"Zwangspause aktiv (noch {pause_info['remaining_minutes']} Min)" + elif is_locked: + current = ProcessLockManager.get_current_process_info() + tooltip = f"Warten Sie bis '{current['type']}' abgeschlossen ist" + else: + tooltip = "Account erstellen" + + self.create_button.setToolTip(tooltip) +``` + +#### Betroffene Module +- `utils/process_lock_manager.py` - **NEU** - Zentrale Lock-Verwaltung +- `utils/error_tracker.py` - **NEU** - Fehler-Tracking mit Zwangspause +- `browser/playwright_manager.py` - Erweitern um Browser-Instanz-Zähler +- `controllers/*_controller.py` - Alle Controller integrieren Lock-Prüfung +- `views/tabs/generator_tab*.py` - UI-Updates für deaktivierte Buttons +- `views/dialogs/` - **NEU** - Pause-Warning Dialog +- `main.py` - Startup-Initialisierung + +### Abhängigkeiten +- Bestehendes Modal-System (ProgressModal, AccountCreationModal, LoginProcessModal) +- Logging-System für Error-Tracking +- File-System für Persistierung (.error_tracking) + +### Risiken & Edge Cases +- **Browser-Absturz:** Counter könnte falsch sein → Reset bei Neustart +- **Zwangspause zu streng:** Nutzer könnte frustriert sein → Klare Kommunikation wichtig +- **Lock bleibt hängen:** Failsafe-Timer (5 Min) im Modal verhindert dies + +### Ausschlüsse +- **KEIN** Manual Override / Force-Unlock Button +- **KEIN** Cooldown zwischen Account-Erstellungen +- **KEINE** Plattform-spezifischen Rate-Limits +- **KEINE** Pause-Überspringen-Funktion + +--- + +## 📋 Weitere Features + +### Feature 3: Server-seitige E-Mail/SMS-Abfrage +**Status:** Geplant +*Details folgen nach Abstimmung* + +--- + +**Letzte Aktualisierung:** 2025-11-09 diff --git a/database/accounts.db b/database/accounts.db index 75acc69..be920ca 100644 Binary files a/database/accounts.db and b/database/accounts.db differ