wohnbot/main.py

139 lines
5.9 KiB
Python
Raw Normal View History

2025-12-31 16:19:14 +01:00
2025-12-27 11:59:04 +01:00
import asyncio
from playwright.async_api import async_playwright
from application_handler import ApplicationHandler
from telegram_bot import TelegramBot
from handlers.wgcompany_notifier import WGCompanyNotifier
import logging
from logging.handlers import RotatingFileHandler
import os
from dotenv import load_dotenv
from state_manager import StateManager
from pathlib import Path
2025-12-31 16:19:14 +01:00
from autoclean_debug import autoclean_debug_material
# --- Environment & Logging Setup ---
# Load environment variables from .env file
load_dotenv()
# Configure logging: file (rotating) + console for Docker visibility, enforce for all modules
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
RotatingFileHandler("data/monitor.log", maxBytes=1 * 1024 * 1024, backupCount=5), # 1 MB per file, 5 backups
logging.StreamHandler()
],
force=True # Enforce for all modules, Python 3.8+
)
logger = logging.getLogger() # Use root logger for universal logging
logger.info("Logging initialized: outputting to both data/monitor.log and console (Docker logs)")
# Interval (seconds) between checks for new listings
CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", 300)) # Default: 300 seconds
def _flush_rotating_file_handlers():
"""Flush all RotatingFileHandlers attached to the root logger."""
root_logger = logging.getLogger()
for handler in root_logger.handlers:
if isinstance(handler, RotatingFileHandler):
handler.flush()
2025-12-27 11:59:04 +01:00
async def main():
logger.info("Starting the bot...")
# Initialize state manager
state_manager = StateManager(Path("data/state.json"))
2025-12-29 22:46:10 +01:00
# --- Playwright browser/context setup ---
playwright = await async_playwright().start()
browser = await playwright.chromium.launch(headless=True)
browser_context = await browser.new_context(
user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
)
logger.info("Playwright browser context initialized.")
# Application handler manages browser/context
2025-12-29 22:46:10 +01:00
app_handler = ApplicationHandler(browser_context, state_manager)
# Set up Telegram bot and inject into handler, passing the main event loop
event_loop = asyncio.get_running_loop()
telegram_bot = TelegramBot(app_handler, event_loop=event_loop)
telegram_bot.start() # Start Telegram command listener for reactivity
app_handler.set_telegram_bot(telegram_bot)
# Start WGCompanyNotifier as a background task
wg_notifier = WGCompanyNotifier(telegram_bot=telegram_bot, refresh_minutes=10)
wg_task = asyncio.create_task(wg_notifier.run())
2025-12-27 11:59:04 +01:00
try:
logger.info(f"Bot is now running. Refreshing every {CHECK_INTERVAL} seconds...")
2025-12-31 16:19:14 +01:00
last_clean = 0
CLEAN_INTERVAL = 48 * 3600 # 48 hours in seconds
while True:
2025-12-31 16:19:14 +01:00
now = asyncio.get_event_loop().time()
# Autoclean debug material every 48 hours
if now - last_clean > CLEAN_INTERVAL:
logger.info("Running autoclean_debug_material (48h interval)...")
try:
deleted = autoclean_debug_material()
logger.info(f"Autocleaned {len(deleted)} debug files.")
except Exception as e:
logger.warning(f"Autoclean failed: {e}")
last_clean = now
current_listings = await app_handler.fetch_listings()
if not current_listings:
logger.warning("No listings fetched")
await asyncio.sleep(CHECK_INTERVAL)
_flush_rotating_file_handlers()
continue
previous_listings = app_handler.load_previous_listings()
if not previous_listings:
2025-12-29 22:46:10 +01:00
logger.info(f"First run - saving {len(current_listings)} listings as baseline and marking as failed applications")
# Mark all as failed applications so /retryfailed can be used
for listing in current_listings:
result = {
"listing_id": listing.get("id"),
"company": app_handler._detect_company(listing.get("link", "")),
"link": listing.get("link"),
"timestamp": str(listing.get("timestamp", "")) or str(listing.get("date", "")) or "",
"success": False,
"message": "First run, not auto-applied. Use /retryfailed to attempt.",
"address": listing.get("address", ""),
"rooms": listing.get("rooms", ""),
"price": listing.get("price", ""),
"retries": 0
}
app_handler.save_application(result)
app_handler.save_listings(current_listings)
await asyncio.sleep(CHECK_INTERVAL)
_flush_rotating_file_handlers()
continue
new_listings = app_handler.find_new_listings(current_listings, previous_listings)
application_results = {}
if new_listings:
logger.info(f"Found {len(new_listings)} new listing(s)")
app_handler.log_listing_times(new_listings)
if app_handler.is_autopilot_enabled():
logger.info("Autopilot enabled - applying to listings...")
application_results = await app_handler.apply_to_listings(new_listings)
app_handler.notify_new_listings(new_listings, application_results)
app_handler.save_listings(current_listings)
await asyncio.sleep(CHECK_INTERVAL)
_flush_rotating_file_handlers()
except (KeyboardInterrupt, SystemExit):
logger.info("Shutting down...")
except Exception as e:
logger.error(f"[MAIN] Error in main loop: {e}")
finally:
2025-12-29 22:46:10 +01:00
await browser.close()
logger.info("Browser closed successfully.")
2025-12-27 11:59:04 +01:00
if __name__ == "__main__":
asyncio.run(main())