laendleimmo-scraper/configure.py

214 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""TUI wizard to configure contact details for the laendleimmo.at scraper."""
import json
import sys
from pathlib import Path
try:
import questionary
from questionary import Style
except ImportError:
sys.exit("questionary is not installed. Run: pip install questionary")
CONFIG_FILE = Path(__file__).parent / "contact_config.json"
STYLE = Style(
[
("qmark", "fg:#00aabb bold"),
("question", "bold"),
("answer", "fg:#00aabb bold"),
("pointer", "fg:#00aabb bold"),
("selected", "fg:#00aabb"),
("separator", "fg:#6c6c6c"),
("instruction", "fg:#858585 italic"),
]
)
DEFAULT_MESSAGE = """\
Sehr geehrte Damen und Herren,
ich interessiere mich sehr für Ihre Immobilie und würde mich über einen \
Besichtigungstermin sehr freuen.
Bitte nehmen Sie Kontakt mit mir auf, damit wir einen Termin vereinbaren können.
Mit freundlichen Grüßen
{name}"""
DIVIDER = "" * 52
def ask(fn, *args, **kwargs):
"""Wrapper that exits cleanly on Ctrl-C."""
result = fn(*args, style=STYLE, **kwargs).ask()
if result is None:
print("\nAborted.")
sys.exit(0)
return result
def validate_email(v: str) -> bool | str:
return True if "@" in v and "." in v.split("@")[-1] else "Enter a valid e-mail address"
def enter_multiline(prompt: str, default: str = "") -> str:
print(f"\n {prompt}")
print(f" (type your message; enter a blank line to finish)")
if default:
print(f" Current value shown below — press Enter on blank line to keep it:\n")
print(" " + default.replace("\n", "\n "))
print()
lines: list[str] = []
try:
while True:
line = input(" > ")
if line == "" and lines and lines[-1] == "":
lines.pop()
break
lines.append(line)
except (KeyboardInterrupt, EOFError):
print("\nAborted.")
sys.exit(0)
text = "\n".join(lines).strip()
return text if text else default
def load_existing() -> dict:
if CONFIG_FILE.exists():
with open(CONFIG_FILE, encoding="utf-8") as f:
return json.load(f)
return {}
def show_summary(cfg: dict) -> None:
print(f"\n {DIVIDER}")
print(" Saved configuration:")
print(f" {DIVIDER}")
print(f" Name : {cfg.get('name', '')}")
print(f" E-mail : {cfg.get('email', '')}")
print(f" Phone : {cfg.get('phone') or '(not set)'}")
msg_preview = cfg.get("message", "")[:60].replace("\n", " ")
print(f" Message : {msg_preview}")
login = cfg.get("login_email", "")
print(f" Login : {login if login else '(not configured)'}")
print(f" {DIVIDER}")
print(f" Config : {CONFIG_FILE}\n")
def main() -> None:
print()
print(f" {DIVIDER}")
print(" ländleimmo.at — Contact Configuration Wizard")
print(f" {DIVIDER}\n")
existing = load_existing()
if existing:
edit = ask(
questionary.confirm,
"Existing config found. Edit it?",
default=True,
)
if not edit:
show_summary(existing)
return
# ── Personal details ────────────────────────────────────────────────── #
print(f"\n {DIVIDER}")
print(" 1 / 3 Personal details")
print(f" {DIVIDER}\n")
name = ask(
questionary.text,
"Full name:",
default=existing.get("name", ""),
)
email = ask(
questionary.text,
"E-mail address:",
default=existing.get("email", ""),
validate=validate_email,
)
phone = ask(
questionary.text,
"Phone number (optional — press Enter to skip):",
default=existing.get("phone", ""),
)
# ── Message ─────────────────────────────────────────────────────────── #
print(f"\n {DIVIDER}")
print(" 2 / 3 Contact message")
print(f" {DIVIDER}")
print(" Tip: use {name} as a placeholder for your name.")
use_default_msg = ask(
questionary.select,
"Message template:",
choices=[
questionary.Choice("Use built-in German template", value="default"),
questionary.Choice("Write / edit message now", value="custom"),
*([questionary.Choice("Keep existing message", value="keep")] if existing.get("message") else []),
],
)
if use_default_msg == "default":
message = DEFAULT_MESSAGE
elif use_default_msg == "keep":
message = existing["message"]
else:
message = enter_multiline(
"Your contact message:",
default=existing.get("message", DEFAULT_MESSAGE),
)
# ── Site login (optional) ────────────────────────────────────────────── #
print(f"\n {DIVIDER}")
print(" 3 / 3 Site login (optional)")
print(f" {DIVIDER}")
print(" If you have an account on laendleimmo.at the scraper can log in")
print(" first so personal data is pre-filled on the contact form.\n")
use_login = ask(
questionary.confirm,
"Configure site login credentials?",
default=bool(existing.get("login_email")),
)
login_email = ""
login_password = ""
if use_login:
login_email = ask(
questionary.text,
"Site login e-mail:",
default=existing.get("login_email", email),
)
login_password = ask(
questionary.password,
"Site login password:",
)
# ── Build and save ───────────────────────────────────────────────────── #
cfg: dict = {
"name": name,
"email": email,
"phone": phone,
"message": message,
}
if use_login:
cfg["login_email"] = login_email
cfg["login_password"] = login_password
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(CONFIG_FILE, "w", encoding="utf-8") as f:
json.dump(cfg, f, indent=2, ensure_ascii=False)
show_summary(cfg)
print(" Configuration saved successfully.\n")
if __name__ == "__main__":
main()