214 lines
6.4 KiB
Python
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()
|