from handlers.base_handler import BaseHandler import logging import asyncio import os from pathlib import Path logger = logging.getLogger(__name__) class DegewoHandler(BaseHandler): def __init__(self, browser_context): self.context = browser_context async def apply(self, listing: dict, result: dict) -> dict: DATA_DIR = Path("data/degewo") DATA_DIR.mkdir(parents=True, exist_ok=True) page = await self.context.new_page() try: logger.info(f"[DEGEWO] Opening page: {listing['link']}") response = await page.goto(listing["link"], wait_until="networkidle") logger.info("[DEGEWO] Page loaded") await asyncio.sleep(2) # 404 detection status = response.status if response else None page_title = await page.title() if status == 404 or (page_title and "404" in page_title): logger.warning(f"[DEGEWO] Listing is down (404): {listing['link']}") result["success"] = False result["message"] = "Listing is no longer available (404). Application impossible. Will not retry." result["permanent_fail"] = True await page.close() return result # Check for 'INSERAT DEAKTIVIERT' (deactivated listing) page_content = await page.content() if "INSERAT DEAKTIVIERT" in page_content or "Inserat deaktiviert" in page_content: logger.warning("[DEGEWO] Listing is deactivated (INSERAT DEAKTIVIERT detected), treating as 404") result["success"] = False result["message"] = "Listing deactivated (404)" result["deactivated"] = True # Mark for removal from retries await page.close() return result # Dismiss cookie banner try: cookie_btn = await page.query_selector('button:has-text("Alle akzeptieren"), #CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll') if cookie_btn and await cookie_btn.is_visible(): await cookie_btn.click() logger.info("[DEGEWO] Dismissed cookie banner") await asyncio.sleep(1) except Exception as e: logger.debug(f"[DEGEWO] Cookie banner dismiss failed: {e}") logger.info("[DEGEWO] Looking for kontaktieren button...") apply_btn = await page.query_selector('a:has-text("kontaktieren"), button:has-text("kontaktieren"), a:has-text("Kontaktieren"), button:has-text("Kontaktieren")') if apply_btn and await apply_btn.is_visible(): logger.info("[DEGEWO] Found kontaktieren button, clicking...") await apply_btn.click() await asyncio.sleep(3) # Degewo uses Wohnungshelden iframe for the application form # Find the iframe and get its URL to navigate directly iframe_element = await page.query_selector('iframe[src*="wohnungshelden.de"]') if iframe_element: iframe_url = await iframe_element.get_attribute('src') logger.info(f"[DEGEWO] Found Wohnungshelden iframe: {iframe_url}") # Navigate to the iframe URL directly in a new page for full access iframe_page = await self.context.new_page() try: await iframe_page.goto(iframe_url, wait_until="networkidle") await asyncio.sleep(2) logger.info("[DEGEWO] Loaded Wohnungshelden application page") # TODO: Implement form-filling and submission logic here finally: await iframe_page.close() else: # No iframe found - try the old approach (fallback for different page structure) logger.warning("[DEGEWO] Wohnungshelden iframe not found, trying direct form...") # TODO: Implement fallback logic here else: result["message"] = "No kontaktieren button found" logger.warning("[DEGEWO] Could not find kontaktieren button") screenshot_path = DATA_DIR / f"degewo_nobtn_{listing['id']}.png" await page.screenshot(path=str(screenshot_path), full_page=True) await page.close() return result except Exception as e: result["success"] = False result["message"] = f"Error: {str(e)}" logger.error(f"[DEGEWO] Exception: {str(e)}") await page.close() return result