Initial commit
Dieser Commit ist enthalten in:
BIN
controllers/__pycache__/account_controller.cpython-310.pyc
Normale Datei
BIN
controllers/__pycache__/account_controller.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
controllers/__pycache__/account_controller.cpython-313.pyc
Normale Datei
BIN
controllers/__pycache__/account_controller.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
controllers/__pycache__/main_controller.cpython-310.pyc
Normale Datei
BIN
controllers/__pycache__/main_controller.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
controllers/__pycache__/main_controller.cpython-313.pyc
Normale Datei
BIN
controllers/__pycache__/main_controller.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
controllers/__pycache__/settings_controller.cpython-310.pyc
Normale Datei
BIN
controllers/__pycache__/settings_controller.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
controllers/__pycache__/settings_controller.cpython-313.pyc
Normale Datei
BIN
controllers/__pycache__/settings_controller.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
149
controllers/account_controller.py
Normale Datei
149
controllers/account_controller.py
Normale Datei
@ -0,0 +1,149 @@
|
||||
"""
|
||||
Controller für die Verwaltung von Accounts.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import csv
|
||||
from datetime import datetime
|
||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
logger = logging.getLogger("account_controller")
|
||||
|
||||
class AccountController(QObject):
|
||||
"""Controller für die Verwaltung von Accounts."""
|
||||
|
||||
def __init__(self, db_manager):
|
||||
super().__init__()
|
||||
self.db_manager = db_manager
|
||||
self.parent_view = None
|
||||
|
||||
def set_parent_view(self, view):
|
||||
"""Setzt die übergeordnete View für Dialoge."""
|
||||
self.parent_view = view
|
||||
|
||||
def on_account_created(self, platform: str, account_data: dict):
|
||||
"""Wird aufgerufen, wenn ein Account erstellt wurde."""
|
||||
account = {
|
||||
"platform": platform.lower(),
|
||||
"username": account_data.get("username", ""),
|
||||
"password": account_data.get("password", ""),
|
||||
"email": account_data.get("email", ""),
|
||||
"phone": account_data.get("phone", ""),
|
||||
"full_name": account_data.get("full_name", ""),
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
|
||||
self.db_manager.add_account(account)
|
||||
logger.info(f"Account in Datenbank gespeichert: {account['username']}")
|
||||
|
||||
# Erfolgsmeldung anzeigen
|
||||
if self.parent_view:
|
||||
QMessageBox.information(
|
||||
self.parent_view,
|
||||
"Erfolg",
|
||||
f"Account erfolgreich erstellt!\n\nBenutzername: {account['username']}\nPasswort: {account['password']}\nE-Mail/Telefon: {account['email'] or account['phone']}"
|
||||
)
|
||||
|
||||
def load_accounts(self, platform=None):
|
||||
"""Lädt Accounts aus der Datenbank."""
|
||||
try:
|
||||
if platform and hasattr(self.db_manager, "get_accounts_by_platform"):
|
||||
accounts = self.db_manager.get_accounts_by_platform(platform.lower())
|
||||
else:
|
||||
accounts = self.db_manager.get_all_accounts()
|
||||
if platform:
|
||||
accounts = [acc for acc in accounts if acc.get("platform", "").lower() == platform.lower()]
|
||||
|
||||
return accounts
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der Accounts: {e}")
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"Fehler beim Laden der Accounts:\n{str(e)}"
|
||||
)
|
||||
return []
|
||||
|
||||
def export_accounts(self, platform=None):
|
||||
"""Exportiert Accounts in eine CSV-Datei."""
|
||||
parent = self.parent_view or None
|
||||
|
||||
file_path, _ = QFileDialog.getSaveFileName(
|
||||
parent,
|
||||
"Konten exportieren",
|
||||
"",
|
||||
"CSV-Dateien (*.csv);;Alle Dateien (*)"
|
||||
)
|
||||
|
||||
if not file_path:
|
||||
return
|
||||
|
||||
try:
|
||||
# Accounts laden
|
||||
accounts = self.load_accounts(platform)
|
||||
|
||||
with open(file_path, "w", newline="", encoding="utf-8") as f:
|
||||
writer = csv.writer(f)
|
||||
|
||||
# Header
|
||||
writer.writerow([
|
||||
"ID", "Plattform", "Benutzername", "Passwort",
|
||||
"E-Mail", "Telefon", "Name", "Erstellt am"
|
||||
])
|
||||
|
||||
# Daten
|
||||
for account in accounts:
|
||||
writer.writerow([
|
||||
account.get("id", ""),
|
||||
account.get("platform", ""),
|
||||
account.get("username", ""),
|
||||
account.get("password", ""),
|
||||
account.get("email", ""),
|
||||
account.get("phone", ""),
|
||||
account.get("full_name", ""),
|
||||
account.get("created_at", "")
|
||||
])
|
||||
|
||||
logger.info(f"Accounts erfolgreich nach {file_path} exportiert")
|
||||
|
||||
if parent:
|
||||
QMessageBox.information(
|
||||
parent,
|
||||
"Export erfolgreich",
|
||||
f"Konten wurden erfolgreich nach {file_path} exportiert."
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Exportieren der Accounts: {e}")
|
||||
if parent:
|
||||
QMessageBox.critical(
|
||||
parent,
|
||||
"Export fehlgeschlagen",
|
||||
f"Fehler beim Exportieren der Konten:\n{str(e)}"
|
||||
)
|
||||
|
||||
def delete_account(self, account_id):
|
||||
"""Löscht einen Account aus der Datenbank."""
|
||||
try:
|
||||
success = self.db_manager.delete_account(account_id)
|
||||
|
||||
if not success:
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"Konto mit ID {account_id} konnte nicht gelöscht werden."
|
||||
)
|
||||
|
||||
return success
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Löschen des Accounts: {e}")
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"Fehler beim Löschen des Kontos:\n{str(e)}"
|
||||
)
|
||||
return False
|
||||
231
controllers/main_controller.py
Normale Datei
231
controllers/main_controller.py
Normale Datei
@ -0,0 +1,231 @@
|
||||
"""
|
||||
Hauptcontroller für die Social Media Account Generator Anwendung.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from PyQt5.QtWidgets import QMessageBox, QApplication
|
||||
|
||||
from views.main_window import MainWindow
|
||||
from controllers.platform_controllers.instagram_controller import InstagramController
|
||||
from controllers.platform_controllers.tiktok_controller import TikTokController
|
||||
from controllers.account_controller import AccountController
|
||||
from controllers.settings_controller import SettingsController
|
||||
|
||||
from database.db_manager import DatabaseManager
|
||||
from utils.proxy_rotator import ProxyRotator
|
||||
from utils.email_handler import EmailHandler
|
||||
from utils.theme_manager import ThemeManager
|
||||
from localization.language_manager import LanguageManager
|
||||
from licensing.license_manager import LicenseManager
|
||||
from updates.update_checker import UpdateChecker
|
||||
|
||||
logger = logging.getLogger("main")
|
||||
|
||||
class MainController:
|
||||
"""Hauptcontroller, der die Anwendung koordiniert."""
|
||||
|
||||
def __init__(self, app):
|
||||
# QApplication Referenz speichern
|
||||
self.app = app
|
||||
|
||||
# Theme Manager initialisieren
|
||||
self.theme_manager = ThemeManager(app)
|
||||
|
||||
# Language Manager initialisieren
|
||||
self.language_manager = LanguageManager(app)
|
||||
|
||||
# Modelle initialisieren
|
||||
self.db_manager = DatabaseManager()
|
||||
self.proxy_rotator = ProxyRotator()
|
||||
self.email_handler = EmailHandler()
|
||||
self.license_manager = LicenseManager()
|
||||
self.update_checker = UpdateChecker()
|
||||
|
||||
# Haupt-View erstellen
|
||||
self.view = MainWindow(self.theme_manager, self.language_manager, self.db_manager)
|
||||
|
||||
# Untercontroller erstellen
|
||||
self.account_controller = AccountController(self.db_manager)
|
||||
self.settings_controller = SettingsController(
|
||||
self.proxy_rotator,
|
||||
self.email_handler,
|
||||
self.license_manager
|
||||
)
|
||||
|
||||
# Plattform-Controller initialisieren
|
||||
self.platform_controllers = {}
|
||||
|
||||
# Instagram Controller hinzufügen
|
||||
self.platform_controllers["instagram"] = InstagramController(
|
||||
self.db_manager,
|
||||
self.proxy_rotator,
|
||||
self.email_handler,
|
||||
self.language_manager
|
||||
)
|
||||
|
||||
# TikTok Controller hinzufügen
|
||||
self.platform_controllers["tiktok"] = TikTokController(
|
||||
self.db_manager,
|
||||
self.proxy_rotator,
|
||||
self.email_handler,
|
||||
self.language_manager
|
||||
)
|
||||
|
||||
# Hier können in Zukunft weitere Controller hinzugefügt werden:
|
||||
# self.platform_controllers["facebook"] = FacebookController(...)
|
||||
# self.platform_controllers["twitter"] = TwitterController(...)
|
||||
# self.platform_controllers["tiktok"] = TikTokController(...)
|
||||
|
||||
# Signals verbinden
|
||||
self.connect_signals()
|
||||
|
||||
# Lizenz überprüfen
|
||||
self.check_license()
|
||||
|
||||
# Auf Updates prüfen
|
||||
self.check_for_updates()
|
||||
|
||||
# Hauptfenster anzeigen
|
||||
self.view.show()
|
||||
|
||||
def connect_signals(self):
|
||||
"""Verbindet alle Signale mit den entsprechenden Slots."""
|
||||
# Plattformauswahl-Signal verbinden
|
||||
self.view.platform_selected.connect(self.on_platform_selected)
|
||||
|
||||
# Zurück-Button verbinden
|
||||
self.view.back_to_selector_requested.connect(self.show_platform_selector)
|
||||
|
||||
# Theme-Toggle verbinden
|
||||
self.view.theme_toggled.connect(self.on_theme_toggled)
|
||||
|
||||
def on_platform_selected(self, platform: str):
|
||||
"""Wird aufgerufen, wenn eine Plattform ausgewählt wird."""
|
||||
logger.info(f"Plattform ausgewählt: {platform}")
|
||||
|
||||
# Aktuelle Plattform setzen
|
||||
self.current_platform = platform.lower()
|
||||
|
||||
# Prüfen, ob die Plattform unterstützt wird
|
||||
if self.current_platform not in self.platform_controllers:
|
||||
logger.error(f"Plattform '{platform}' wird nicht unterstützt")
|
||||
QMessageBox.critical(
|
||||
self.view,
|
||||
"Nicht unterstützt",
|
||||
f"Die Plattform '{platform}' ist noch nicht implementiert."
|
||||
)
|
||||
return
|
||||
|
||||
# Plattformspezifischen Controller abrufen
|
||||
platform_controller = self.platform_controllers.get(self.current_platform)
|
||||
|
||||
# Plattform-View initialisieren
|
||||
self.view.init_platform_ui(platform, platform_controller)
|
||||
|
||||
# Tab-Hooks verbinden
|
||||
self.connect_tab_hooks(platform_controller)
|
||||
|
||||
# Plattformspezifische Ansicht anzeigen
|
||||
self.view.show_platform_ui()
|
||||
|
||||
def on_theme_toggled(self):
|
||||
"""Wird aufgerufen, wenn das Theme gewechselt wird."""
|
||||
if self.theme_manager:
|
||||
theme_name = self.theme_manager.get_current_theme()
|
||||
logger.info(f"Theme gewechselt zu: {theme_name}")
|
||||
|
||||
# Hier kann zusätzliche Logik für Theme-Wechsel hinzugefügt werden
|
||||
# z.B. UI-Elemente aktualisieren, die nicht automatisch aktualisiert werden
|
||||
|
||||
def connect_tab_hooks(self, platform_controller):
|
||||
"""Verbindet die Tab-Hooks mit dem Plattform-Controller."""
|
||||
# Generator-Tab-Hooks
|
||||
if hasattr(platform_controller, "get_generator_tab"):
|
||||
generator_tab = platform_controller.get_generator_tab()
|
||||
generator_tab.account_created.connect(self.account_controller.on_account_created)
|
||||
|
||||
# Einstellungen-Tab-Hooks
|
||||
if hasattr(platform_controller, "get_settings_tab"):
|
||||
settings_tab = platform_controller.get_settings_tab()
|
||||
settings_tab.proxy_settings_saved.connect(self.settings_controller.save_proxy_settings)
|
||||
settings_tab.proxy_tested.connect(self.settings_controller.test_proxy)
|
||||
settings_tab.email_settings_saved.connect(self.settings_controller.save_email_settings)
|
||||
settings_tab.email_tested.connect(self.settings_controller.test_email)
|
||||
settings_tab.license_activated.connect(self.settings_controller.activate_license)
|
||||
|
||||
def show_platform_selector(self):
|
||||
"""Zeigt den Plattform-Selektor an."""
|
||||
logger.info("Zurück zur Plattformauswahl")
|
||||
self.view.show_platform_selector()
|
||||
if hasattr(self.view, "platform_selector"):
|
||||
self.view.platform_selector.load_accounts()
|
||||
|
||||
def check_license(self):
|
||||
"""Überprüft, ob eine gültige Lizenz vorhanden ist."""
|
||||
is_licensed = self.license_manager.is_licensed()
|
||||
|
||||
if not is_licensed:
|
||||
license_info = self.license_manager.get_license_info()
|
||||
status = license_info.get("status_text", "Inaktiv")
|
||||
|
||||
# Wenn keine Lizenz vorhanden ist, zeigen wir eine Warnung an
|
||||
QMessageBox.warning(
|
||||
self.view,
|
||||
"Keine gültige Lizenz",
|
||||
f"Status: {status}\n\nBitte aktivieren Sie eine Lizenz, um die Software zu nutzen."
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def check_for_updates(self):
|
||||
"""Prüft auf Updates."""
|
||||
try:
|
||||
update_info = self.update_checker.check_for_updates()
|
||||
|
||||
if update_info["has_update"]:
|
||||
reply = QMessageBox.question(
|
||||
self.view,
|
||||
"Update verfügbar",
|
||||
f"Eine neue Version ist verfügbar: {update_info['latest_version']}\n"
|
||||
f"(Aktuelle Version: {update_info['current_version']})\n\n"
|
||||
f"Release-Datum: {update_info['release_date']}\n"
|
||||
f"Release-Notes:\n{update_info['release_notes']}\n\n"
|
||||
"Möchten Sie das Update jetzt herunterladen?",
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.Yes
|
||||
)
|
||||
|
||||
if reply == QMessageBox.Yes:
|
||||
self.download_update(update_info)
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei der Update-Prüfung: {e}")
|
||||
|
||||
def download_update(self, update_info):
|
||||
"""Lädt ein Update herunter."""
|
||||
try:
|
||||
download_result = self.update_checker.download_update(
|
||||
update_info["download_url"],
|
||||
update_info["latest_version"]
|
||||
)
|
||||
|
||||
if download_result["success"]:
|
||||
QMessageBox.information(
|
||||
self.view,
|
||||
"Download erfolgreich",
|
||||
f"Update wurde heruntergeladen: {download_result['file_path']}\n\n"
|
||||
"Bitte schließen Sie die Anwendung und führen Sie das Update aus."
|
||||
)
|
||||
else:
|
||||
QMessageBox.warning(
|
||||
self.view,
|
||||
"Download fehlgeschlagen",
|
||||
f"Fehler beim Herunterladen des Updates:\n{download_result['error']}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Herunterladen des Updates: {e}")
|
||||
QMessageBox.critical(
|
||||
self.view,
|
||||
"Fehler",
|
||||
f"Fehler beim Herunterladen des Updates:\n{str(e)}"
|
||||
)
|
||||
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
132
controllers/platform_controllers/base_controller.py
Normale Datei
132
controllers/platform_controllers/base_controller.py
Normale Datei
@ -0,0 +1,132 @@
|
||||
"""
|
||||
Basis-Controller für Plattform-spezifische Funktionalität.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
from views.tabs.generator_tab import GeneratorTab
|
||||
from views.tabs.accounts_tab import AccountsTab
|
||||
from views.tabs.settings_tab import SettingsTab
|
||||
|
||||
class BasePlatformController(QObject):
|
||||
"""Basis-Controller-Klasse für Plattformspezifische Logik."""
|
||||
|
||||
def __init__(self, platform_name, db_manager, proxy_rotator, email_handler, language_manager=None):
|
||||
super().__init__()
|
||||
self.platform_name = platform_name
|
||||
self.logger = logging.getLogger(f"{platform_name.lower()}_controller")
|
||||
|
||||
# Modelle
|
||||
self.db_manager = db_manager
|
||||
self.proxy_rotator = proxy_rotator
|
||||
self.email_handler = email_handler
|
||||
self.language_manager = language_manager
|
||||
|
||||
# Tabs
|
||||
self._generator_tab = None
|
||||
self._accounts_tab = None
|
||||
self._settings_tab = None
|
||||
|
||||
# Plattformspezifische Initialisierungen
|
||||
self.init_platform()
|
||||
|
||||
def init_platform(self):
|
||||
"""
|
||||
Initialisiert plattformspezifische Komponenten.
|
||||
Diese Methode sollte von Unterklassen überschrieben werden.
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_generator_tab(self):
|
||||
"""Gibt den Generator-Tab zurück oder erstellt ihn bei Bedarf."""
|
||||
if not self._generator_tab:
|
||||
self._generator_tab = self.create_generator_tab()
|
||||
return self._generator_tab
|
||||
|
||||
def get_accounts_tab(self):
|
||||
"""Gibt den Accounts-Tab zurück oder erstellt ihn bei Bedarf."""
|
||||
if not self._accounts_tab:
|
||||
self._accounts_tab = self.create_accounts_tab()
|
||||
return self._accounts_tab
|
||||
|
||||
def get_settings_tab(self):
|
||||
"""Gibt den Settings-Tab zurück oder erstellt ihn bei Bedarf."""
|
||||
if not self._settings_tab:
|
||||
self._settings_tab = self.create_settings_tab()
|
||||
return self._settings_tab
|
||||
|
||||
|
||||
def create_generator_tab(self):
|
||||
"""
|
||||
Erstellt den Generator-Tab.
|
||||
Diese Methode sollte von Unterklassen überschrieben werden.
|
||||
"""
|
||||
return GeneratorTab(self.platform_name, self.language_manager)
|
||||
|
||||
def create_accounts_tab(self):
|
||||
"""
|
||||
Erstellt den Accounts-Tab.
|
||||
Diese Methode sollte von Unterklassen überschrieben werden.
|
||||
"""
|
||||
return AccountsTab(self.platform_name, self.db_manager, self.language_manager)
|
||||
|
||||
def create_settings_tab(self):
|
||||
"""
|
||||
Erstellt den Settings-Tab.
|
||||
Diese Methode sollte von Unterklassen überschrieben werden.
|
||||
"""
|
||||
return SettingsTab(
|
||||
self.platform_name,
|
||||
self.proxy_rotator,
|
||||
self.email_handler,
|
||||
self.language_manager
|
||||
)
|
||||
|
||||
|
||||
def start_account_creation(self, params):
|
||||
"""
|
||||
Startet die Account-Erstellung.
|
||||
Diese Methode sollte von Unterklassen überschrieben werden.
|
||||
|
||||
Args:
|
||||
params: Parameter für die Account-Erstellung
|
||||
"""
|
||||
self.logger.info(f"Account-Erstellung für {self.platform_name} gestartet")
|
||||
# In Unterklassen implementieren
|
||||
|
||||
def validate_inputs(self, inputs):
|
||||
"""
|
||||
Validiert die Eingaben für die Account-Erstellung.
|
||||
|
||||
Args:
|
||||
inputs: Eingaben für die Account-Erstellung
|
||||
|
||||
Returns:
|
||||
(bool, str): (Ist gültig, Fehlermeldung falls nicht gültig)
|
||||
"""
|
||||
# Basis-Validierungen
|
||||
if not inputs.get("full_name"):
|
||||
return False, "Bitte geben Sie einen vollständigen Namen ein."
|
||||
|
||||
# Alter prüfen
|
||||
age_text = inputs.get("age_text", "")
|
||||
if not age_text:
|
||||
return False, "Bitte geben Sie ein Alter ein."
|
||||
|
||||
# Alter muss eine Zahl sein
|
||||
try:
|
||||
age = int(age_text)
|
||||
inputs["age"] = age # Füge das konvertierte Alter zu den Parametern hinzu
|
||||
except ValueError:
|
||||
return False, "Das Alter muss eine ganze Zahl sein."
|
||||
|
||||
# Alter-Bereich prüfen
|
||||
if age < 13 or age > 99:
|
||||
return False, "Das Alter muss zwischen 13 und 99 liegen."
|
||||
|
||||
# Telefonnummer prüfen, falls erforderlich
|
||||
if inputs.get("registration_method") == "phone" and not inputs.get("phone_number"):
|
||||
return False, "Telefonnummer erforderlich für Registrierung via Telefon."
|
||||
|
||||
return True, ""
|
||||
275
controllers/platform_controllers/instagram_controller.py
Normale Datei
275
controllers/platform_controllers/instagram_controller.py
Normale Datei
@ -0,0 +1,275 @@
|
||||
"""
|
||||
Controller für Instagram-spezifische Funktionalität.
|
||||
Mit TextSimilarity-Integration für robusteres UI-Element-Matching.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import random
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, QObject
|
||||
|
||||
from controllers.platform_controllers.base_controller import BasePlatformController
|
||||
from views.tabs.generator_tab import GeneratorTab
|
||||
from views.tabs.accounts_tab import AccountsTab
|
||||
from views.tabs.settings_tab import SettingsTab
|
||||
|
||||
from social_networks.instagram.instagram_automation import InstagramAutomation
|
||||
from utils.text_similarity import TextSimilarity
|
||||
|
||||
logger = logging.getLogger("instagram_controller")
|
||||
|
||||
class InstagramWorkerThread(QThread):
|
||||
"""Thread für die Instagram-Account-Erstellung."""
|
||||
|
||||
# Signale
|
||||
update_signal = pyqtSignal(str)
|
||||
log_signal = pyqtSignal(str)
|
||||
progress_signal = pyqtSignal(int)
|
||||
finished_signal = pyqtSignal(dict)
|
||||
error_signal = pyqtSignal(str)
|
||||
|
||||
def __init__(self, params):
|
||||
super().__init__()
|
||||
self.params = params
|
||||
self.running = True
|
||||
|
||||
# TextSimilarity für robustes Fehler-Matching
|
||||
self.text_similarity = TextSimilarity(default_threshold=0.7)
|
||||
|
||||
# Fehler-Patterns für robustes Fehler-Matching
|
||||
self.error_patterns = [
|
||||
"Fehler", "Error", "Fehlgeschlagen", "Failed", "Problem", "Issue",
|
||||
"Nicht möglich", "Not possible", "Bitte versuchen Sie es erneut",
|
||||
"Please try again", "Konnte nicht", "Could not", "Timeout"
|
||||
]
|
||||
|
||||
def run(self):
|
||||
"""Führt die Account-Erstellung aus."""
|
||||
try:
|
||||
self.log_signal.emit("Instagram-Account-Erstellung gestartet...")
|
||||
self.progress_signal.emit(10)
|
||||
|
||||
# Instagram-Automation initialisieren
|
||||
automation = InstagramAutomation(
|
||||
headless=self.params.get("headless", False),
|
||||
use_proxy=self.params.get("use_proxy", False),
|
||||
proxy_type=self.params.get("proxy_type"),
|
||||
save_screenshots=True,
|
||||
debug=self.params.get("debug", False),
|
||||
email_domain=self.params.get("email_domain", "z5m7q9dk3ah2v1plx6ju.com")
|
||||
)
|
||||
|
||||
self.update_signal.emit("Instagram-Automation initialisiert")
|
||||
self.progress_signal.emit(20)
|
||||
|
||||
# Account registrieren
|
||||
self.log_signal.emit(f"Registriere Account für: {self.params['full_name']}")
|
||||
|
||||
registration_method = self.params.get("registration_method", "email")
|
||||
phone_number = self.params.get("phone_number")
|
||||
|
||||
# Account registrieren
|
||||
result = automation.register_account(
|
||||
full_name=self.params["full_name"],
|
||||
age=self.params["age"],
|
||||
registration_method=registration_method,
|
||||
phone_number=phone_number,
|
||||
**self.params.get("additional_params", {})
|
||||
)
|
||||
|
||||
self.progress_signal.emit(100)
|
||||
|
||||
if result["success"]:
|
||||
self.log_signal.emit("Account erfolgreich erstellt!")
|
||||
self.finished_signal.emit(result)
|
||||
else:
|
||||
# Robuste Fehlerbehandlung mit TextSimilarity
|
||||
error_msg = result.get("error", "Unbekannter Fehler")
|
||||
|
||||
# Versuche, Fehler nutzerfreundlicher zu interpretieren
|
||||
user_friendly_error = self._interpret_error(error_msg)
|
||||
|
||||
self.log_signal.emit(f"Fehler bei der Account-Erstellung: {user_friendly_error}")
|
||||
self.error_signal.emit(user_friendly_error)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler im Worker-Thread: {e}")
|
||||
self.log_signal.emit(f"Schwerwiegender Fehler: {str(e)}")
|
||||
self.error_signal.emit(str(e))
|
||||
self.progress_signal.emit(0)
|
||||
|
||||
def _interpret_error(self, error_msg: str) -> str:
|
||||
"""
|
||||
Interpretiert Fehlermeldungen und gibt eine benutzerfreundlichere Version zurück.
|
||||
Verwendet TextSimilarity für robusteres Fehler-Matching.
|
||||
|
||||
Args:
|
||||
error_msg: Die ursprüngliche Fehlermeldung
|
||||
|
||||
Returns:
|
||||
str: Benutzerfreundliche Fehlermeldung
|
||||
"""
|
||||
# Bekannte Fehlermuster und deren Interpretationen
|
||||
error_interpretations = {
|
||||
"captcha": "Instagram hat einen Captcha-Test angefordert. Versuchen Sie es später erneut oder nutzen Sie einen anderen Proxy.",
|
||||
"verification": "Es gab ein Problem mit der Verifizierung des Accounts. Bitte prüfen Sie die E-Mail-Einstellungen.",
|
||||
"proxy": "Problem mit der Proxy-Verbindung. Bitte prüfen Sie Ihre Proxy-Einstellungen.",
|
||||
"timeout": "Zeitüberschreitung bei der Verbindung. Bitte überprüfen Sie Ihre Internetverbindung.",
|
||||
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig.",
|
||||
"password": "Das Passwort erfüllt nicht die Anforderungen von Instagram.",
|
||||
"email": "Die E-Mail-Adresse konnte nicht verwendet werden. Bitte nutzen Sie eine andere E-Mail-Domain.",
|
||||
"phone": "Die Telefonnummer konnte nicht für die Registrierung verwendet werden."
|
||||
}
|
||||
|
||||
# Versuche, den Fehler zu kategorisieren
|
||||
for pattern, interpretation in error_interpretations.items():
|
||||
for error_term in self.error_patterns:
|
||||
if (pattern in error_msg.lower() or
|
||||
self.text_similarity.is_similar(error_term, error_msg, threshold=0.7)):
|
||||
return interpretation
|
||||
|
||||
# Fallback: Originale Fehlermeldung zurückgeben
|
||||
return error_msg
|
||||
|
||||
def stop(self):
|
||||
"""Stoppt den Thread."""
|
||||
self.running = False
|
||||
self.terminate()
|
||||
|
||||
class InstagramController(BasePlatformController):
|
||||
"""Controller für Instagram-spezifische Funktionalität."""
|
||||
|
||||
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager=None):
|
||||
super().__init__("Instagram", db_manager, proxy_rotator, email_handler, language_manager)
|
||||
self.worker_thread = None
|
||||
|
||||
# TextSimilarity für robustes UI-Element-Matching
|
||||
self.text_similarity = TextSimilarity(default_threshold=0.75)
|
||||
|
||||
def create_generator_tab(self):
|
||||
"""Erstellt den Instagram-Generator-Tab."""
|
||||
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
|
||||
|
||||
# Instagram-spezifische Anpassungen
|
||||
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
||||
|
||||
# Signale verbinden
|
||||
generator_tab.start_requested.connect(self.start_account_creation)
|
||||
generator_tab.stop_requested.connect(self.stop_account_creation)
|
||||
|
||||
return generator_tab
|
||||
|
||||
def start_account_creation(self, params):
|
||||
"""Startet die Instagram-Account-Erstellung."""
|
||||
super().start_account_creation(params)
|
||||
|
||||
# Validiere Eingaben
|
||||
is_valid, error_msg = self.validate_inputs(params)
|
||||
if not is_valid:
|
||||
self.get_generator_tab().show_error(error_msg)
|
||||
return
|
||||
|
||||
# UI aktualisieren
|
||||
generator_tab = self.get_generator_tab()
|
||||
generator_tab.set_running(True)
|
||||
generator_tab.clear_log()
|
||||
generator_tab.set_progress(0)
|
||||
|
||||
# Worker-Thread starten
|
||||
self.worker_thread = InstagramWorkerThread(params)
|
||||
self.worker_thread.update_signal.connect(lambda msg: generator_tab.set_status(msg))
|
||||
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
|
||||
self.worker_thread.error_signal.connect(lambda msg: (generator_tab.show_error(msg), generator_tab.set_running(False)))
|
||||
self.worker_thread.finished_signal.connect(lambda result: self.handle_account_created(result))
|
||||
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
|
||||
|
||||
self.worker_thread.start()
|
||||
|
||||
def stop_account_creation(self):
|
||||
"""Stoppt die Instagram-Account-Erstellung."""
|
||||
if self.worker_thread and self.worker_thread.isRunning():
|
||||
self.worker_thread.stop()
|
||||
generator_tab = self.get_generator_tab()
|
||||
generator_tab.add_log("Account-Erstellung wurde abgebrochen")
|
||||
generator_tab.set_running(False)
|
||||
generator_tab.set_progress(0)
|
||||
|
||||
def handle_account_created(self, result):
|
||||
"""Verarbeitet erfolgreich erstellte Accounts."""
|
||||
generator_tab = self.get_generator_tab()
|
||||
generator_tab.set_running(False)
|
||||
|
||||
# Account-Daten aus dem Ergebnis holen
|
||||
account_data = result.get("account_data", {})
|
||||
|
||||
# Account-Erfolgsereignis auslösen
|
||||
generator_tab.account_created.emit(self.platform_name, account_data)
|
||||
|
||||
# Account in der Datenbank speichern
|
||||
self.save_account_to_db(account_data)
|
||||
|
||||
def save_account_to_db(self, account_data):
|
||||
"""Speichert einen erstellten Account in der Datenbank."""
|
||||
account = {
|
||||
"platform": self.platform_name.lower(),
|
||||
"username": account_data.get("username", ""),
|
||||
"password": account_data.get("password", ""),
|
||||
"email": account_data.get("email", ""),
|
||||
"phone": account_data.get("phone", ""),
|
||||
"full_name": account_data.get("full_name", ""),
|
||||
"created_at": time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
|
||||
self.db_manager.add_account(account)
|
||||
logger.info(f"Account in Datenbank gespeichert: {account['username']}")
|
||||
|
||||
def validate_inputs(self, inputs):
|
||||
"""
|
||||
Validiert die Eingaben für die Account-Erstellung.
|
||||
Verwendet TextSimilarity für robustere Validierung.
|
||||
"""
|
||||
# Basis-Validierungen von BasePlatformController verwenden
|
||||
valid, error_msg = super().validate_inputs(inputs)
|
||||
if not valid:
|
||||
return valid, error_msg
|
||||
|
||||
# Instagram-spezifische Validierungen
|
||||
age = inputs.get("age", 0)
|
||||
if age < 13: # Änderung von 14 auf 13
|
||||
return False, "Das Alter muss mindestens 13 sein (Instagram-Anforderung)."
|
||||
|
||||
# E-Mail-Domain-Validierung
|
||||
if inputs.get("registration_method") == "email":
|
||||
email_domain = inputs.get("email_domain", "")
|
||||
# Blacklist von bekannten problematischen Domains
|
||||
blacklisted_domains = ["temp-mail.org", "guerrillamail.com", "maildrop.cc"]
|
||||
|
||||
# Prüfe mit TextSimilarity auf Ähnlichkeit mit Blacklist
|
||||
for domain in blacklisted_domains:
|
||||
if self.text_similarity.is_similar(email_domain, domain, threshold=0.8):
|
||||
return False, f"Die E-Mail-Domain '{email_domain}' kann problematisch für die Instagram-Registrierung sein. Bitte verwenden Sie eine andere Domain."
|
||||
|
||||
return True, ""
|
||||
|
||||
def get_form_field_label(self, field_type: str) -> str:
|
||||
"""
|
||||
Gibt einen Label-Text für ein Formularfeld basierend auf dem Feldtyp zurück.
|
||||
|
||||
Args:
|
||||
field_type: Typ des Formularfelds
|
||||
|
||||
Returns:
|
||||
str: Label-Text für das Formularfeld
|
||||
"""
|
||||
# Mapping von Feldtypen zu Labels
|
||||
field_labels = {
|
||||
"full_name": "Vollständiger Name",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"email": "E-Mail-Adresse",
|
||||
"phone": "Telefonnummer",
|
||||
"age": "Alter",
|
||||
"birthday": "Geburtsdatum"
|
||||
}
|
||||
|
||||
return field_labels.get(field_type, field_type.capitalize())
|
||||
277
controllers/platform_controllers/tiktok_controller.py
Normale Datei
277
controllers/platform_controllers/tiktok_controller.py
Normale Datei
@ -0,0 +1,277 @@
|
||||
"""
|
||||
Controller für TikTok-spezifische Funktionalität.
|
||||
Mit TextSimilarity-Integration für robusteres UI-Element-Matching.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import random
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, QObject
|
||||
|
||||
from controllers.platform_controllers.base_controller import BasePlatformController
|
||||
from views.tabs.generator_tab import GeneratorTab
|
||||
from views.tabs.accounts_tab import AccountsTab
|
||||
from views.tabs.settings_tab import SettingsTab
|
||||
|
||||
from social_networks.tiktok.tiktok_automation import TikTokAutomation
|
||||
from utils.text_similarity import TextSimilarity
|
||||
|
||||
logger = logging.getLogger("tiktok_controller")
|
||||
|
||||
class TikTokWorkerThread(QThread):
|
||||
"""Thread für die TikTok-Account-Erstellung."""
|
||||
|
||||
# Signale
|
||||
update_signal = pyqtSignal(str)
|
||||
log_signal = pyqtSignal(str)
|
||||
progress_signal = pyqtSignal(int)
|
||||
finished_signal = pyqtSignal(dict)
|
||||
error_signal = pyqtSignal(str)
|
||||
|
||||
def __init__(self, params):
|
||||
super().__init__()
|
||||
self.params = params
|
||||
self.running = True
|
||||
|
||||
# TextSimilarity für robustes Fehler-Matching
|
||||
self.text_similarity = TextSimilarity(default_threshold=0.7)
|
||||
|
||||
# Fehler-Patterns für robustes Fehler-Matching
|
||||
self.error_patterns = [
|
||||
"Fehler", "Error", "Fehlgeschlagen", "Failed", "Problem", "Issue",
|
||||
"Nicht möglich", "Not possible", "Bitte versuchen Sie es erneut",
|
||||
"Please try again", "Konnte nicht", "Could not", "Timeout"
|
||||
]
|
||||
|
||||
def run(self):
|
||||
"""Führt die Account-Erstellung aus."""
|
||||
try:
|
||||
self.log_signal.emit("TikTok-Account-Erstellung gestartet...")
|
||||
self.progress_signal.emit(10)
|
||||
|
||||
# TikTok-Automation initialisieren
|
||||
automation = TikTokAutomation(
|
||||
headless=self.params.get("headless", False),
|
||||
use_proxy=self.params.get("use_proxy", False),
|
||||
proxy_type=self.params.get("proxy_type"),
|
||||
save_screenshots=True,
|
||||
debug=self.params.get("debug", False),
|
||||
email_domain=self.params.get("email_domain", "z5m7q9dk3ah2v1plx6ju.com")
|
||||
)
|
||||
|
||||
self.update_signal.emit("TikTok-Automation initialisiert")
|
||||
self.progress_signal.emit(20)
|
||||
|
||||
# Account registrieren
|
||||
self.log_signal.emit(f"Registriere Account für: {self.params['full_name']}")
|
||||
|
||||
registration_method = self.params.get("registration_method", "email")
|
||||
phone_number = self.params.get("phone_number")
|
||||
|
||||
# Account registrieren
|
||||
result = automation.register_account(
|
||||
full_name=self.params["full_name"],
|
||||
age=self.params["age"],
|
||||
registration_method=registration_method,
|
||||
phone_number=phone_number,
|
||||
**self.params.get("additional_params", {})
|
||||
)
|
||||
|
||||
self.progress_signal.emit(100)
|
||||
|
||||
if result["success"]:
|
||||
self.log_signal.emit("Account erfolgreich erstellt!")
|
||||
self.finished_signal.emit(result)
|
||||
else:
|
||||
# Robuste Fehlerbehandlung mit TextSimilarity
|
||||
error_msg = result.get("error", "Unbekannter Fehler")
|
||||
|
||||
# Versuche, Fehler nutzerfreundlicher zu interpretieren
|
||||
user_friendly_error = self._interpret_error(error_msg)
|
||||
|
||||
self.log_signal.emit(f"Fehler bei der Account-Erstellung: {user_friendly_error}")
|
||||
self.error_signal.emit(user_friendly_error)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler im Worker-Thread: {e}")
|
||||
self.log_signal.emit(f"Schwerwiegender Fehler: {str(e)}")
|
||||
self.error_signal.emit(str(e))
|
||||
self.progress_signal.emit(0)
|
||||
|
||||
def _interpret_error(self, error_msg: str) -> str:
|
||||
"""
|
||||
Interpretiert Fehlermeldungen und gibt eine benutzerfreundlichere Version zurück.
|
||||
Verwendet TextSimilarity für robusteres Fehler-Matching.
|
||||
|
||||
Args:
|
||||
error_msg: Die ursprüngliche Fehlermeldung
|
||||
|
||||
Returns:
|
||||
str: Benutzerfreundliche Fehlermeldung
|
||||
"""
|
||||
# Bekannte Fehlermuster und deren Interpretationen
|
||||
error_interpretations = {
|
||||
"captcha": "TikTok hat einen Captcha-Test angefordert. Versuchen Sie es später erneut oder nutzen Sie einen anderen Proxy.",
|
||||
"verification": "Es gab ein Problem mit der Verifizierung des Accounts. Bitte prüfen Sie die E-Mail-Einstellungen.",
|
||||
"proxy": "Problem mit der Proxy-Verbindung. Bitte prüfen Sie Ihre Proxy-Einstellungen.",
|
||||
"timeout": "Zeitüberschreitung bei der Verbindung. Bitte überprüfen Sie Ihre Internetverbindung.",
|
||||
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig.",
|
||||
"password": "Das Passwort erfüllt nicht die Anforderungen von TikTok.",
|
||||
"email": "Die E-Mail-Adresse konnte nicht verwendet werden. Bitte nutzen Sie eine andere E-Mail-Domain.",
|
||||
"phone": "Die Telefonnummer konnte nicht für die Registrierung verwendet werden.",
|
||||
"age": "Das eingegebene Alter erfüllt nicht die Anforderungen von TikTok.",
|
||||
"too_many_attempts": "Zu viele Registrierungsversuche. Bitte warten Sie und versuchen Sie es später erneut."
|
||||
}
|
||||
|
||||
# Versuche, den Fehler zu kategorisieren
|
||||
for pattern, interpretation in error_interpretations.items():
|
||||
for error_term in self.error_patterns:
|
||||
if (pattern in error_msg.lower() or
|
||||
self.text_similarity.is_similar(error_term, error_msg, threshold=0.7)):
|
||||
return interpretation
|
||||
|
||||
# Fallback: Originale Fehlermeldung zurückgeben
|
||||
return error_msg
|
||||
|
||||
def stop(self):
|
||||
"""Stoppt den Thread."""
|
||||
self.running = False
|
||||
self.terminate()
|
||||
|
||||
class TikTokController(BasePlatformController):
|
||||
"""Controller für TikTok-spezifische Funktionalität."""
|
||||
|
||||
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager=None):
|
||||
super().__init__("TikTok", db_manager, proxy_rotator, email_handler, language_manager)
|
||||
self.worker_thread = None
|
||||
|
||||
# TextSimilarity für robustes UI-Element-Matching
|
||||
self.text_similarity = TextSimilarity(default_threshold=0.75)
|
||||
|
||||
def create_generator_tab(self):
|
||||
"""Erstellt den TikTok-Generator-Tab."""
|
||||
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
|
||||
|
||||
# TikTok-spezifische Anpassungen
|
||||
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
||||
|
||||
# Signale verbinden
|
||||
generator_tab.start_requested.connect(self.start_account_creation)
|
||||
generator_tab.stop_requested.connect(self.stop_account_creation)
|
||||
|
||||
return generator_tab
|
||||
|
||||
def start_account_creation(self, params):
|
||||
"""Startet die TikTok-Account-Erstellung."""
|
||||
super().start_account_creation(params)
|
||||
|
||||
# Validiere Eingaben
|
||||
is_valid, error_msg = self.validate_inputs(params)
|
||||
if not is_valid:
|
||||
self.get_generator_tab().show_error(error_msg)
|
||||
return
|
||||
|
||||
# UI aktualisieren
|
||||
generator_tab = self.get_generator_tab()
|
||||
generator_tab.set_running(True)
|
||||
generator_tab.clear_log()
|
||||
generator_tab.set_progress(0)
|
||||
|
||||
# Worker-Thread starten
|
||||
self.worker_thread = TikTokWorkerThread(params)
|
||||
self.worker_thread.update_signal.connect(lambda msg: generator_tab.set_status(msg))
|
||||
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
|
||||
self.worker_thread.error_signal.connect(lambda msg: (generator_tab.show_error(msg), generator_tab.set_running(False)))
|
||||
self.worker_thread.finished_signal.connect(lambda result: self.handle_account_created(result))
|
||||
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
|
||||
|
||||
self.worker_thread.start()
|
||||
|
||||
def stop_account_creation(self):
|
||||
"""Stoppt die TikTok-Account-Erstellung."""
|
||||
if self.worker_thread and self.worker_thread.isRunning():
|
||||
self.worker_thread.stop()
|
||||
generator_tab = self.get_generator_tab()
|
||||
generator_tab.add_log("Account-Erstellung wurde abgebrochen")
|
||||
generator_tab.set_running(False)
|
||||
generator_tab.set_progress(0)
|
||||
|
||||
def handle_account_created(self, result):
|
||||
"""Verarbeitet erfolgreich erstellte Accounts."""
|
||||
generator_tab = self.get_generator_tab()
|
||||
generator_tab.set_running(False)
|
||||
|
||||
# Account-Daten aus dem Ergebnis holen
|
||||
account_data = result.get("account_data", {})
|
||||
|
||||
# Account-Erfolgsereignis auslösen
|
||||
generator_tab.account_created.emit(self.platform_name, account_data)
|
||||
|
||||
# Account in der Datenbank speichern
|
||||
self.save_account_to_db(account_data)
|
||||
|
||||
def save_account_to_db(self, account_data):
|
||||
"""Speichert einen erstellten Account in der Datenbank."""
|
||||
account = {
|
||||
"platform": self.platform_name.lower(),
|
||||
"username": account_data.get("username", ""),
|
||||
"password": account_data.get("password", ""),
|
||||
"email": account_data.get("email", ""),
|
||||
"phone": account_data.get("phone", ""),
|
||||
"full_name": account_data.get("full_name", ""),
|
||||
"created_at": time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
|
||||
self.db_manager.add_account(account)
|
||||
logger.info(f"Account in Datenbank gespeichert: {account['username']}")
|
||||
|
||||
def validate_inputs(self, inputs):
|
||||
"""
|
||||
Validiert die Eingaben für die Account-Erstellung.
|
||||
Verwendet TextSimilarity für robustere Validierung.
|
||||
"""
|
||||
# Basis-Validierungen von BasePlatformController verwenden
|
||||
valid, error_msg = super().validate_inputs(inputs)
|
||||
if not valid:
|
||||
return valid, error_msg
|
||||
|
||||
# TikTok-spezifische Validierungen
|
||||
age = inputs.get("age", 0)
|
||||
if age < 13:
|
||||
return False, "Das Alter muss mindestens 13 sein (TikTok-Anforderung)."
|
||||
|
||||
# E-Mail-Domain-Validierung
|
||||
if inputs.get("registration_method") == "email":
|
||||
email_domain = inputs.get("email_domain", "")
|
||||
# Blacklist von bekannten problematischen Domains
|
||||
blacklisted_domains = ["temp-mail.org", "guerrillamail.com", "maildrop.cc"]
|
||||
|
||||
# Prüfe mit TextSimilarity auf Ähnlichkeit mit Blacklist
|
||||
for domain in blacklisted_domains:
|
||||
if self.text_similarity.is_similar(email_domain, domain, threshold=0.8):
|
||||
return False, f"Die E-Mail-Domain '{email_domain}' kann problematisch für die TikTok-Registrierung sein. Bitte verwenden Sie eine andere Domain."
|
||||
|
||||
return True, ""
|
||||
|
||||
def get_form_field_label(self, field_type: str) -> str:
|
||||
"""
|
||||
Gibt einen Label-Text für ein Formularfeld basierend auf dem Feldtyp zurück.
|
||||
|
||||
Args:
|
||||
field_type: Typ des Formularfelds
|
||||
|
||||
Returns:
|
||||
str: Label-Text für das Formularfeld
|
||||
"""
|
||||
# Mapping von Feldtypen zu Labels
|
||||
field_labels = {
|
||||
"full_name": "Vollständiger Name",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"email": "E-Mail-Adresse",
|
||||
"phone": "Telefonnummer",
|
||||
"age": "Alter",
|
||||
"birthday": "Geburtsdatum"
|
||||
}
|
||||
|
||||
return field_labels.get(field_type, field_type.capitalize())
|
||||
294
controllers/settings_controller.py
Normale Datei
294
controllers/settings_controller.py
Normale Datei
@ -0,0 +1,294 @@
|
||||
"""
|
||||
Controller für die Verwaltung von Einstellungen.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import random
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
logger = logging.getLogger("settings_controller")
|
||||
|
||||
class SettingsController(QObject):
|
||||
"""Controller für die Verwaltung von Einstellungen."""
|
||||
|
||||
def __init__(self, proxy_rotator, email_handler, license_manager):
|
||||
super().__init__()
|
||||
self.proxy_rotator = proxy_rotator
|
||||
self.email_handler = email_handler
|
||||
self.license_manager = license_manager
|
||||
self.parent_view = None
|
||||
|
||||
def set_parent_view(self, view):
|
||||
"""Setzt die übergeordnete View für Dialoge."""
|
||||
self.parent_view = view
|
||||
|
||||
def load_proxy_settings(self):
|
||||
"""Lädt die Proxy-Einstellungen."""
|
||||
try:
|
||||
proxy_config = self.proxy_rotator.get_config() or {}
|
||||
|
||||
settings = {
|
||||
"ipv4_proxies": proxy_config.get("ipv4", []),
|
||||
"ipv6_proxies": proxy_config.get("ipv6", []),
|
||||
"mobile_proxies": proxy_config.get("mobile", []),
|
||||
"mobile_api": proxy_config.get("mobile_api", {})
|
||||
}
|
||||
|
||||
return settings
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der Proxy-Einstellungen: {e}")
|
||||
return {}
|
||||
|
||||
def save_proxy_settings(self, settings):
|
||||
"""Speichert die Proxy-Einstellungen."""
|
||||
try:
|
||||
# IPv4 Proxies
|
||||
ipv4_proxies = settings.get("ipv4_proxies", [])
|
||||
if isinstance(ipv4_proxies, str):
|
||||
ipv4_proxies = [line.strip() for line in ipv4_proxies.splitlines() if line.strip()]
|
||||
|
||||
# IPv6 Proxies
|
||||
ipv6_proxies = settings.get("ipv6_proxies", [])
|
||||
if isinstance(ipv6_proxies, str):
|
||||
ipv6_proxies = [line.strip() for line in ipv6_proxies.splitlines() if line.strip()]
|
||||
|
||||
# Mobile Proxies
|
||||
mobile_proxies = settings.get("mobile_proxies", [])
|
||||
if isinstance(mobile_proxies, str):
|
||||
mobile_proxies = [line.strip() for line in mobile_proxies.splitlines() if line.strip()]
|
||||
|
||||
# API Keys
|
||||
mobile_api = settings.get("mobile_api", {})
|
||||
|
||||
# Konfiguration aktualisieren
|
||||
self.proxy_rotator.update_config({
|
||||
"ipv4": ipv4_proxies,
|
||||
"ipv6": ipv6_proxies,
|
||||
"mobile": mobile_proxies,
|
||||
"mobile_api": mobile_api
|
||||
})
|
||||
|
||||
logger.info("Proxy-Einstellungen gespeichert")
|
||||
|
||||
if self.parent_view:
|
||||
QMessageBox.information(
|
||||
self.parent_view,
|
||||
"Erfolg",
|
||||
"Proxy-Einstellungen wurden gespeichert."
|
||||
)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Speichern der Proxy-Einstellungen: {e}")
|
||||
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"Proxy-Einstellungen konnten nicht gespeichert werden:\n{str(e)}"
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
def test_proxy(self, proxy_type):
|
||||
"""Testet einen zufälligen Proxy des ausgewählten Typs."""
|
||||
try:
|
||||
# Überprüfe, ob Proxies konfiguriert sind
|
||||
proxies = self.proxy_rotator.get_proxies_by_type(proxy_type)
|
||||
if not proxies:
|
||||
if self.parent_view:
|
||||
QMessageBox.warning(
|
||||
self.parent_view,
|
||||
"Keine Proxies",
|
||||
f"Keine {proxy_type.upper()}-Proxies konfiguriert.\nBitte fügen Sie Proxies in den Einstellungen hinzu."
|
||||
)
|
||||
return False
|
||||
|
||||
# Zufälligen Proxy auswählen
|
||||
proxy = random.choice(proxies)
|
||||
|
||||
# Proxy testen
|
||||
result = self.proxy_rotator.test_proxy(proxy_type)
|
||||
|
||||
if result["success"]:
|
||||
if self.parent_view:
|
||||
QMessageBox.information(
|
||||
self.parent_view,
|
||||
"Proxy-Test erfolgreich",
|
||||
f"IP: {result['ip']}\nLand: {result['country'] or 'Unbekannt'}\nAntwortzeit: {result['response_time']:.2f}s"
|
||||
)
|
||||
return True
|
||||
else:
|
||||
if self.parent_view:
|
||||
QMessageBox.warning(
|
||||
self.parent_view,
|
||||
"Proxy-Test fehlgeschlagen",
|
||||
f"Fehler: {result['error']}"
|
||||
)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Testen des Proxy: {e}")
|
||||
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"Fehler beim Testen des Proxy:\n{str(e)}"
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
def load_email_settings(self):
|
||||
"""Lädt die E-Mail-Einstellungen."""
|
||||
try:
|
||||
email_config = self.email_handler.get_config() or {}
|
||||
|
||||
settings = {
|
||||
"imap_server": email_config.get("imap_server", ""),
|
||||
"imap_port": email_config.get("imap_port", 993),
|
||||
"imap_user": email_config.get("imap_user", ""),
|
||||
"imap_pass": email_config.get("imap_pass", "")
|
||||
}
|
||||
|
||||
return settings
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der E-Mail-Einstellungen: {e}")
|
||||
return {}
|
||||
|
||||
def save_email_settings(self, settings):
|
||||
"""Speichert die E-Mail-Einstellungen."""
|
||||
try:
|
||||
# Einstellungen aktualisieren
|
||||
self.email_handler.update_config(settings)
|
||||
|
||||
logger.info("E-Mail-Einstellungen gespeichert")
|
||||
|
||||
if self.parent_view:
|
||||
QMessageBox.information(
|
||||
self.parent_view,
|
||||
"Erfolg",
|
||||
"E-Mail-Einstellungen wurden gespeichert."
|
||||
)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Speichern der E-Mail-Einstellungen: {e}")
|
||||
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"E-Mail-Einstellungen konnten nicht gespeichert werden:\n{str(e)}"
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
def test_email(self, settings=None):
|
||||
"""Testet die E-Mail-Verbindung."""
|
||||
try:
|
||||
if settings:
|
||||
# Temporär Einstellungen aktualisieren
|
||||
self.email_handler.update_credentials(
|
||||
settings.get("imap_user", ""),
|
||||
settings.get("imap_pass", "")
|
||||
)
|
||||
self.email_handler.update_server(
|
||||
settings.get("imap_server", ""),
|
||||
settings.get("imap_port", 993)
|
||||
)
|
||||
|
||||
# Verbindung testen
|
||||
result = self.email_handler.test_connection()
|
||||
|
||||
if result["success"]:
|
||||
if self.parent_view:
|
||||
QMessageBox.information(
|
||||
self.parent_view,
|
||||
"E-Mail-Test erfolgreich",
|
||||
f"Verbindung zu {result['server']}:{result['port']} hergestellt.\nGefundene Postfächer: {result['mailbox_count']}"
|
||||
)
|
||||
return True
|
||||
else:
|
||||
if self.parent_view:
|
||||
QMessageBox.warning(
|
||||
self.parent_view,
|
||||
"E-Mail-Test fehlgeschlagen",
|
||||
f"Fehler: {result['error']}"
|
||||
)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Testen der E-Mail-Verbindung: {e}")
|
||||
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"Fehler beim Testen der E-Mail-Verbindung:\n{str(e)}"
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
def load_license_info(self):
|
||||
"""Lädt die Lizenzinformationen."""
|
||||
try:
|
||||
license_info = self.license_manager.get_license_info()
|
||||
return license_info
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der Lizenzinformationen: {e}")
|
||||
return {}
|
||||
|
||||
def activate_license(self, license_key):
|
||||
"""Aktiviert eine Lizenz."""
|
||||
try:
|
||||
success, message = self.license_manager.activate_license(license_key)
|
||||
|
||||
if success:
|
||||
if self.parent_view:
|
||||
QMessageBox.information(
|
||||
self.parent_view,
|
||||
"Lizenz aktiviert",
|
||||
message
|
||||
)
|
||||
else:
|
||||
if self.parent_view:
|
||||
QMessageBox.warning(
|
||||
self.parent_view,
|
||||
"Lizenzaktivierung fehlgeschlagen",
|
||||
message
|
||||
)
|
||||
|
||||
return success, message
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei der Lizenzaktivierung: {e}")
|
||||
|
||||
if self.parent_view:
|
||||
QMessageBox.critical(
|
||||
self.parent_view,
|
||||
"Fehler",
|
||||
f"Fehler bei der Lizenzaktivierung:\n{str(e)}"
|
||||
)
|
||||
|
||||
return False, str(e)
|
||||
|
||||
def check_license(self):
|
||||
"""Überprüft, ob eine gültige Lizenz vorhanden ist."""
|
||||
try:
|
||||
is_licensed = self.license_manager.is_licensed()
|
||||
|
||||
if not is_licensed and self.parent_view:
|
||||
license_info = self.license_manager.get_license_info()
|
||||
status = license_info.get("status_text", "Inaktiv")
|
||||
|
||||
QMessageBox.warning(
|
||||
self.parent_view,
|
||||
"Keine gültige Lizenz",
|
||||
f"Status: {status}\n\nBitte aktivieren Sie eine Lizenz, um die Software zu nutzen."
|
||||
)
|
||||
|
||||
return is_licensed
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei der Lizenzprüfung: {e}")
|
||||
return False
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren