Compare commits

..

No commits in common. "9dee262fbfc1c8adb63f64e939d92f271295c5f2" and "2b16e52a53c1ae6f958dc0e05cd0557e551ee6c4" have entirely different histories.

3 changed files with 13 additions and 98 deletions

View file

@ -1,4 +1,4 @@
# wohn-bot # inberlin-monitor
A Python bot that monitors Berlin's public housing portal (inberlinwohnen.de) and WG rooms (wgcompany.de). Sends Telegram notifications when new listings appear and can automatically apply to some listings. A Python bot that monitors Berlin's public housing portal (inberlinwohnen.de) and WG rooms (wgcompany.de). Sends Telegram notifications when new listings appear and can automatically apply to some listings.
@ -12,23 +12,17 @@ A Python bot that monitors Berlin's public housing portal (inberlinwohnen.de) an
## Auto-Apply Support ## Auto-Apply Support
All six housing companies monitored by this bot now support the autopilot (automatic application) feature. Use autopilot with care — automatic form submission is destructive and may send many requests if configured incorrectly. The auto-apply feature is experimental and only works for some housing companies:
| Company | Status | Notes | | Company | Status | Notes |
|---------|--------|-------| |---------|--------|-------|
| HOWOGE | Working | Fully automated and tested | | HOWOGE | Working | Tested and functional |
| Degewo | Working | Uses Wohnungshelden portal; automated | | Degewo | Experimental | Uses Wohnungshelden portal |
| Stadt und Land | Working | Embedded form handled automatically | | Stadt und Land | Experimental | Uses Wohnungshelden portal |
| Gewobag | Working | Wohnungshelden iframe handled automatically | | Gewobag | Not working | Needs implementation |
| Gesobau | Working | Automated form submission implemented | | Gesobau | Not working | Needs implementation |
| WBM | Working | Automated form submission implemented | | WBM | Not working | Needs implementation |
| WGcompany | Monitoring only | WGcompany monitoring only (no autopilot) | | WGcompany | Not supported | Monitoring only, no auto-apply |
Recommended precautions:
- Run with `/autopilot off` while testing new selectors or after changing config.
- Inspect `data/applications.json` and saved screenshots in `data/` after enabling autopilot.
- Respect site terms of use and rate limits; set `CHECK_INTERVAL` appropriately.
## Setup ## Setup

View file

@ -1,17 +1,9 @@
services: services:
wohnbot: inberlin-monitor:
build: . build: .
container_name: wohnbot container_name: inberlin-monitor
restart: unless-stopped restart: unless-stopped
env_file: env_file:
- .env - .env
volumes: volumes:
- /srv/dev-disk-by-uuid-a920d9c0-dfc1-4a58-ae4d-92cf88ff04a5/docker-app/wohnbot/data:/data:rw - ./data:/data
networks:
proxy-network:
aliases:
- wohnbot
networks:
proxy-network:
external: true

View file

@ -8,7 +8,7 @@ import html
import threading import threading
import time import time
import csv import csv
from datetime import datetime, timedelta from datetime import datetime
from pathlib import Path from pathlib import Path
import requests import requests
@ -58,65 +58,6 @@ APPLICATIONS_FILE = DATA_DIR / "applications.json"
# WGcompany specific files # WGcompany specific files
WGCOMPANY_LISTINGS_FILE = DATA_DIR / "wgcompany_listings.json" WGCOMPANY_LISTINGS_FILE = DATA_DIR / "wgcompany_listings.json"
def _cleanup_old_files(png_hours: int = 24, log_days: int = 7):
"""Remove PNG files older than `png_hours` and prune log lines older than `log_days` days.
Runs best-effort and logs exceptions to the logger.
"""
try:
now = datetime.utcnow()
# Remove old PNGs in DATA_DIR
png_cutoff = now - timedelta(hours=png_hours)
removed_pngs = 0
for p in DATA_DIR.glob("*.png"):
try:
mtime = datetime.fromtimestamp(p.stat().st_mtime)
if mtime < png_cutoff:
p.unlink()
removed_pngs += 1
except Exception:
logger.exception(f"Error while checking/removing PNG: {p}")
if removed_pngs:
logger.info(f"Removed {removed_pngs} PNG(s) older than {png_hours} hours")
# Prune logfile lines older than log_days
if LOG_FILE.exists():
cutoff_log = now - timedelta(days=log_days)
kept_lines = []
try:
with open(LOG_FILE, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
# Expect logging lines starting with 'YYYY-MM-DD HH:MM:SS,ms - '
m = re.match(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d+)\s+-\s+", line)
if m:
try:
ts = datetime.strptime(m.group(1), "%Y-%m-%d %H:%M:%S,%f")
if ts >= cutoff_log:
kept_lines.append(line)
except Exception:
# If parsing fails, keep the line
kept_lines.append(line)
else:
# Keep non-standard lines
kept_lines.append(line)
# Atomically replace the logfile with kept lines
if kept_lines:
tmp = LOG_FILE.with_suffix(".tmp")
with open(tmp, "w", encoding="utf-8") as f:
f.writelines(kept_lines)
tmp.replace(LOG_FILE)
else:
# No recent lines; truncate the file
with open(LOG_FILE, "w", encoding="utf-8") as f:
f.truncate(0)
logger.info(f"Pruned logfile, kept {len(kept_lines)} lines from last {log_days} days")
except Exception:
logger.exception("Error while pruning logfile")
except Exception:
logger.exception("Unexpected error in cleanup task")
WGCOMPANY_TIMING_FILE = DATA_DIR / "wgcompany_times.csv" WGCOMPANY_TIMING_FILE = DATA_DIR / "wgcompany_times.csv"
# Setup logging # Setup logging
@ -2141,9 +2082,6 @@ def main():
logger.info(f"InBerlin Autopilot: {'ENABLED' if inberlin_monitor.is_autopilot_enabled() else 'DISABLED'}") logger.info(f"InBerlin Autopilot: {'ENABLED' if inberlin_monitor.is_autopilot_enabled() else 'DISABLED'}")
logger.info(f"WGcompany: {'ENABLED' if WGCOMPANY_ENABLED else 'DISABLED'}") logger.info(f"WGcompany: {'ENABLED' if WGCOMPANY_ENABLED else 'DISABLED'}")
# Run periodic cleanup hourly
last_cleanup = 0
while True: while True:
# Check InBerlinWohnen # Check InBerlinWohnen
try: try:
@ -2151,15 +2089,6 @@ def main():
except Exception as e: except Exception as e:
logger.error(f"InBerlin check failed: {e}") logger.error(f"InBerlin check failed: {e}")
# Periodic cleanup: remove PNGs older than 24h and prune logs older than 7 days
try:
if time.time() - last_cleanup > 3600: # every hour
logger.info("Running periodic cleanup (old PNGs, prune logs)")
_cleanup_old_files(png_hours=24, log_days=7)
last_cleanup = time.time()
except Exception:
logger.exception("Cleanup failed")
# Check WGcompany # Check WGcompany
if wgcompany_monitor: if wgcompany_monitor:
try: try: