83 lines
4.0 KiB
Python
83 lines
4.0 KiB
Python
"""Interactive read-only SmartTime UI probe (separate from the app).
|
|
|
|
Reads JSON commands from stdin: snapshot, login, click (text), goto (url), quit.
|
|
Artifacts stay local; no credential or cookie values are printed.
|
|
"""
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "stempelbot"))
|
|
|
|
from playwright.sync_api import sync_playwright
|
|
from settings import settings
|
|
|
|
|
|
def main():
|
|
output = ROOT / ".local" / "smarttime" / datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
output.mkdir(parents=True, exist_ok=True)
|
|
with sync_playwright() as pw:
|
|
browser = pw.chromium.launch(headless=True)
|
|
context = browser.new_context(ignore_https_errors=True, locale="de-DE", viewport={"width": 1440, "height": 1100})
|
|
page = context.new_page()
|
|
page.set_default_timeout(8000)
|
|
page.goto(settings.SMART_TIME_URL, wait_until="domcontentloaded", timeout=20000)
|
|
print(f"ARTIFACTS {output}", flush=True)
|
|
count = 0
|
|
while True:
|
|
try:
|
|
command = json.loads(input("COMMAND> "))
|
|
action = command["action"]
|
|
if action == "quit":
|
|
break
|
|
if action == "evaluate":
|
|
print(json.dumps(page.evaluate(command["js"]), ensure_ascii=True), flush=True)
|
|
continue
|
|
if action == "run":
|
|
import runpy
|
|
runpy.run_path(command["file"])["run"](page, output)
|
|
continue
|
|
if action == "login":
|
|
page.locator(command["user"]).fill(settings.USERNAME)
|
|
page.locator(command["password"]).fill(settings.SMART_TIME_PASSWORD)
|
|
page.locator(command["password"]).press("Enter")
|
|
elif action == "click":
|
|
page.get_by_text(command["text"], exact=True).click()
|
|
elif action == "selector":
|
|
page.locator(command["selector"]).click()
|
|
elif action == "fill":
|
|
page.locator(command["selector"]).fill(command["value"])
|
|
page.locator(command["selector"]).press("Enter")
|
|
elif action == "goto":
|
|
page.goto(command["url"], wait_until="domcontentloaded", timeout=20000)
|
|
elif action == "inspect":
|
|
print(page.locator(command["selector"]).evaluate_all("els => els.map(e => e.outerHTML)"), flush=True)
|
|
count += 1
|
|
name = f"{count:02d}-{action}"
|
|
page.screenshot(path=str(output / f"{name}.png"), full_page=True, timeout=10000)
|
|
# Clone DOM and remove input values before persisting snapshots.
|
|
html = page.evaluate("""() => { const root = document.documentElement.cloneNode(true);
|
|
root.querySelectorAll('input').forEach(e => e.removeAttribute('value'));
|
|
return '<!DOCTYPE html>' + root.outerHTML; }""")
|
|
(output / f"{name}.html").write_text(html, encoding="utf-8")
|
|
print("URL", page.url, flush=True)
|
|
print(page.locator("body").inner_text(timeout=5000), flush=True)
|
|
if action == "inspect" or "login" in page.url:
|
|
print("CONTROLS", page.locator("input,button,a,select,[role=tab]").evaluate_all("els => els.map(e => ({tag:e.tagName, name:e.getAttribute('name'), type:e.getAttribute('type'), id:e.id, text:e.innerText, title:e.title, href:e.getAttribute('href')}))"), flush=True)
|
|
except EOFError:
|
|
break
|
|
except Exception as exc:
|
|
message = str(exc)
|
|
for secret in (settings.SMART_TIME_PASSWORD, settings.PASSWORD):
|
|
if secret:
|
|
message = message.replace(secret, "***")
|
|
print(type(exc).__name__, message, flush=True)
|
|
context.close()
|
|
browser.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|