229 Zeilen
9.0 KiB
Python
229 Zeilen
9.0 KiB
Python
"""Service for applying browser protection during automation."""
|
|
from typing import Optional
|
|
from playwright.sync_api import Page
|
|
|
|
from domain.value_objects.browser_protection_style import BrowserProtectionStyle, ProtectionLevel
|
|
|
|
|
|
class BrowserProtectionService:
|
|
"""Handles browser protection during automation to prevent user interference."""
|
|
|
|
SHIELD_ELEMENT_ID = "accountforge-shield"
|
|
PROTECTION_STYLE_ID = "accountforge-protection-styles"
|
|
|
|
def protect_browser(self, page: Page, style: BrowserProtectionStyle) -> None:
|
|
"""Apply protection to the browser page based on the configured style."""
|
|
if style.level == ProtectionLevel.NONE:
|
|
return
|
|
|
|
# Generate and inject protection script
|
|
script = self._generate_protection_script(style)
|
|
page.evaluate(script)
|
|
|
|
# Speichere Script für Wiederanwendung
|
|
escaped_script = script.replace('`', '\\`')
|
|
page.evaluate(f"""
|
|
window.__accountforge_protection = `{escaped_script}`;
|
|
""")
|
|
|
|
def remove_protection(self, page: Page) -> None:
|
|
"""Remove all protection from the browser page."""
|
|
page.evaluate(f"""
|
|
// Remove shield element
|
|
const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}');
|
|
if (shield) shield.remove();
|
|
|
|
// Remove style element
|
|
const styles = document.getElementById('{self.PROTECTION_STYLE_ID}');
|
|
if (styles) styles.remove();
|
|
|
|
// Note: Event listeners will be removed on page navigation
|
|
""")
|
|
|
|
def _generate_protection_script(self, style: BrowserProtectionStyle) -> str:
|
|
"""Generate JavaScript code for protection based on style configuration."""
|
|
script_parts = []
|
|
|
|
# Create shield overlay
|
|
if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]:
|
|
script_parts.append(self._create_shield_script(style))
|
|
|
|
# Add visual effects
|
|
if style.show_border or (style.level == ProtectionLevel.LIGHT):
|
|
script_parts.append(self._create_visual_effects_script(style))
|
|
|
|
# Block interactions
|
|
if style.level in [ProtectionLevel.MEDIUM, ProtectionLevel.STRONG]:
|
|
script_parts.append(self._create_interaction_blocker_script())
|
|
|
|
return "\n".join(script_parts)
|
|
|
|
def _create_shield_script(self, style: BrowserProtectionStyle) -> str:
|
|
"""Create the main shield overlay element."""
|
|
badge_positions = {
|
|
"top-left": "top: 20px; left: 20px;",
|
|
"top-right": "top: 20px; right: 20px;",
|
|
"bottom-left": "bottom: 20px; left: 20px;",
|
|
"bottom-right": "bottom: 20px; right: 20px;"
|
|
}
|
|
badge_position_css = badge_positions.get(style.badge_position, badge_positions["top-right"])
|
|
|
|
blur_css = "backdrop-filter: blur(2px);" if style.blur_effect else ""
|
|
|
|
return f"""
|
|
// Create shield overlay
|
|
const shield = document.createElement('div');
|
|
shield.id = '{self.SHIELD_ELEMENT_ID}';
|
|
shield.style.cssText = `
|
|
position: fixed;
|
|
top: 0; left: 0;
|
|
width: 100vw; height: 100vh;
|
|
background: {style.get_overlay_color()};
|
|
{blur_css}
|
|
z-index: 2147483647;
|
|
cursor: not-allowed;
|
|
user-select: none;
|
|
pointer-events: all;
|
|
`;
|
|
|
|
// Add info badge if configured
|
|
if ({str(style.show_badge).lower()}) {{
|
|
const badge = document.createElement('div');
|
|
badge.style.cssText = `
|
|
position: absolute;
|
|
{badge_position_css}
|
|
background: rgba(220, 38, 38, 0.95);
|
|
color: white;
|
|
padding: 12px 24px;
|
|
border-radius: 8px;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
animation: fadeIn 0.3s ease-in;
|
|
`;
|
|
badge.textContent = '{style.badge_text}';
|
|
shield.appendChild(badge);
|
|
}}
|
|
|
|
document.documentElement.appendChild(shield);
|
|
"""
|
|
|
|
def _create_visual_effects_script(self, style: BrowserProtectionStyle) -> str:
|
|
"""Create visual effects like animated border."""
|
|
return f"""
|
|
// Add styles for visual effects
|
|
const styleElement = document.createElement('style');
|
|
styleElement.id = '{self.PROTECTION_STYLE_ID}';
|
|
styleElement.textContent = `
|
|
@keyframes pulse {{
|
|
0% {{ opacity: 0.4; }}
|
|
50% {{ opacity: 0.8; }}
|
|
100% {{ opacity: 0.4; }}
|
|
}}
|
|
|
|
@keyframes fadeIn {{
|
|
from {{ opacity: 0; transform: translateY(-10px); }}
|
|
to {{ opacity: 1; transform: translateY(0); }}
|
|
}}
|
|
|
|
{self._get_border_css(style) if style.show_border else ''}
|
|
`;
|
|
document.head.appendChild(styleElement);
|
|
|
|
{self._get_border_element_script(style) if style.show_border else ''}
|
|
"""
|
|
|
|
def _get_border_css(self, style: BrowserProtectionStyle) -> str:
|
|
"""Get CSS for animated border."""
|
|
return f"""
|
|
#accountforge-border {{
|
|
position: fixed;
|
|
top: 0; left: 0;
|
|
width: 100%; height: 100%;
|
|
border: 3px solid {style.border_color};
|
|
box-shadow: inset 0 0 30px rgba(255, 0, 0, 0.2);
|
|
pointer-events: none;
|
|
animation: pulse 2s infinite;
|
|
z-index: 2147483646;
|
|
}}
|
|
"""
|
|
|
|
def _get_border_element_script(self, style: BrowserProtectionStyle) -> str:
|
|
"""Get script to create border element."""
|
|
return f"""
|
|
// Create animated border
|
|
const border = document.createElement('div');
|
|
border.id = 'accountforge-border';
|
|
document.body.appendChild(border);
|
|
"""
|
|
|
|
def _create_interaction_blocker_script(self) -> str:
|
|
"""Create script to block all user interactions."""
|
|
return f"""
|
|
// Verhindere das Shield selbst entfernt zu werden
|
|
(function() {{
|
|
// Prüfe ob Shield schon existiert
|
|
if (document.getElementById('{self.SHIELD_ELEMENT_ID}')) {{
|
|
return;
|
|
}}
|
|
|
|
// Block all interaction events
|
|
const blockedEvents = [
|
|
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove',
|
|
'keydown', 'keypress', 'keyup',
|
|
'touchstart', 'touchend', 'touchmove',
|
|
'contextmenu', 'wheel', 'scroll', 'input', 'change', 'focus'
|
|
];
|
|
|
|
const eventBlocker = function(e) {{
|
|
// Prüfe ob das Event vom Shield selbst kommt
|
|
const shield = document.getElementById('{self.SHIELD_ELEMENT_ID}');
|
|
if (shield && (e.target === shield || shield.contains(e.target))) {{
|
|
return;
|
|
}}
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
e.stopImmediatePropagation();
|
|
return false;
|
|
}};
|
|
|
|
// Add event listeners with capture phase
|
|
blockedEvents.forEach(eventType => {{
|
|
document.addEventListener(eventType, eventBlocker, true);
|
|
window.addEventListener(eventType, eventBlocker, true);
|
|
}});
|
|
|
|
// Disable text selection
|
|
document.body.style.userSelect = 'none';
|
|
document.body.style.webkitUserSelect = 'none';
|
|
document.body.style.mozUserSelect = 'none';
|
|
document.body.style.msUserSelect = 'none';
|
|
|
|
// Disable all input fields
|
|
const disableInputs = () => {{
|
|
document.querySelectorAll('input, textarea, select, button').forEach(el => {{
|
|
el.style.pointerEvents = 'none';
|
|
el.setAttribute('disabled', 'true');
|
|
el.setAttribute('readonly', 'true');
|
|
}});
|
|
}};
|
|
|
|
// Initial disable
|
|
disableInputs();
|
|
|
|
// Re-disable on DOM changes
|
|
const inputObserver = new MutationObserver(disableInputs);
|
|
inputObserver.observe(document.body, {{ childList: true, subtree: true }});
|
|
|
|
// Disable drag
|
|
document.ondragstart = function() {{ return false; }};
|
|
document.onselectstart = function() {{ return false; }};
|
|
|
|
// Prevent focus on any element
|
|
document.addEventListener('focusin', function(e) {{
|
|
e.target.blur();
|
|
}}, true);
|
|
}})();
|
|
""" |