commit 4007ae3908212d9413d6aefacb4a205efd44475e
Author: MrJensK <jens.se@icloud.com>
AuthorDate: Fri Aug 28 20:09:35 2026 +0200
Commit: MrJensK <jens.se@icloud.com>
CommitDate: Fri Aug 28 20:09:35 2026 +0200
updates
---
README.md | 17 +++++++++++------
TODO.md | 26 ++++++++++++++++++++++----
main.py | 10 +---------
rpa/auth.py | 27 +++++++++++++++++++--------
rpa/codegen.py | 2 +-
rpa/config.py | 16 ++++------------
rpa/discover.py | 6 +++---
rpa/flow_engine.py | 4 +++-
rpa/notify.py | 6 +++---
rpa/scraper.py | 19 -------------------
webui/templates/index.html | 7 ++++++-
11 files changed, 73 insertions(+), 67 deletions(-)
diff --git a/README.md b/README.md
index 454ef4c..306950b 100644
--- a/README.md
+++ b/README.md
@@ -173,10 +173,14 @@ igen. Det gäller oavsett om flödet körs via webui:t, `python -m
rpa.codegen`, eller ett eget script byggt på `rpa/browser.py`.
`rpa/auth.py` innehåller ett generellt mönster för att vänta ut en manuell
-inloggning: `wait_for_login(page, timeout_s)` pollar sidan tills någon av
-`config.LOGGED_IN_INDICATORS` (fri textmatchning) syns, eller ger upp efter
-en timeout. Anpassa `config.START_URL` och `config.LOGGED_IN_INDICATORS`
-per sajt.
+inloggning: `wait_for_login(page, timeout_s, indicators=...)` pollar sidan
+tills någon textsträng i `indicators` (fri textmatchning) syns, eller ger
+upp efter en timeout. I webui:t sätts detta direkt i steget "Vänta på
+inloggning" (fältet för inloggad-text, komma-separerat) - alltså per
+flöde/sajt, utan att röra Python-kod. Lämnas fältet tomt faller det
+tillbaka på standardvärdet `config.LOGGED_IN_INDICATORS`, som du kan
+anpassa tillsammans med `config.START_URL` om du kör via `main.py`/eget
+script istället för webui:t.
## Schemaläggning
@@ -213,8 +217,9 @@ nya/ändrade poster.
Använd det som mall när du bygger en ny fristående automation för en
annan sajt - byt ut `scraper.py`s sid-specifika logik, `config.py`s
-URL/textindikatorer, och `notify.py`s meddelandetext. Behöver du inte
-diff/notis-mönstret alls räcker det oftast med ett flöde i webui:t.
+`START_URL`/`LOGGED_IN_INDICATORS`, och `notify.py`s meddelandetext.
+Behöver du inte diff/notis-mönstret alls räcker det oftast med ett flöde
+i webui:t.
## Att tänka på
diff --git a/TODO.md b/TODO.md
index 1a1a8a9..56e8b6d 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,4 +1,22 @@
-Lägga till att kunna köra custom python kod mellan
-ex. lista extraheras
-lista körs genom custom script för att få ut datan
-lägg som vairabel
+1. Desktop kontroll - styra datorn utanför web.
+ - Förslag: pywinauto (UIA-backend) som förstahandsval - hittar
+ kontroller via namn/automation-id/control-type, samma princip som
+ Playwright-selectorsen på browser-sidan. pyautogui som fallback
+ (bildmatchning/koordinater) för appar utan tillgänglighets-API.
+ - Nya stegtyper i flow_engine.py: desktop_click, desktop_type,
+ desktop_wait_for_window, desktop_screenshot, ev. desktop_click_image
+ (pyautogui-fallback). Samma by/value-mönster som befintliga
+ browser-steg.
+ - run_flow behöver hålla en desktop-referens vid sidan av page så ett
+ flöde kan blanda browser- och desktop-steg i samma sekvens.
+ - Lazy import av pywinauto inuti stegfunktionen (Windows-only) så
+ flow_engine.py inte kraschar vid import på icke-Windows.
+ - rpa/desktop_inspect.py i stil med discover.py - dumpar
+ control identifiers för aktivt fönster, för att hitta rätt
+ selectors manuellt (måste köras på Windows-maskinen med målappen).
+ - Webui: ny stegrupp "Desktop" i FIELD_DEFS/STEP_TYPES i index.html.
+ - OBS: om syftet är att automatisera BankID:s egen dialog (PIN/
+ godkännande) - troligen härdat mot UI Automation och mot BankIDs
+ användarvillkor. Klargör exakt vad som ska styras innan
+ implementation.
+
diff --git a/main.py b/main.py
index 11b1ccd..b33188e 100644
--- a/main.py
+++ b/main.py
@@ -6,7 +6,7 @@ from rpa import config, state
from rpa.auth import ensure_logged_in
from rpa.browser import get_page, launch_context
from rpa.notify import notify_new_items
-from rpa.scraper import download_attachments, goto_egenremisser, list_egenremisser
+from rpa.scraper import download_attachments, list_egenremisser
def run() -> None:
@@ -16,14 +16,6 @@ def run() -> None:
ensure_logged_in(page)
- if not goto_egenremisser(page):
- print(
- "Kunde inte hitta en länk till egenremisser automatiskt. "
- "Navigera dit manuellt i webbläsarfönstret, tryck sedan "
- "Enter här för att fortsätta."
- )
- input()
-
items = list_egenremisser(page)
print(f"Hittade {len(items)} post(er) på sidan.")
diff --git a/rpa/auth.py b/rpa/auth.py
index e2ddb0a..9bf9ec9 100644
--- a/rpa/auth.py
+++ b/rpa/auth.py
@@ -1,24 +1,34 @@
-from typing import Callable
+from typing import Callable, Optional, Sequence
from playwright.sync_api import Page
from . import config
-def is_logged_in(page: Page) -> bool:
- for text in config.LOGGED_IN_INDICATORS:
+def is_logged_in(page: Page, indicators: Optional[Sequence[str]] = None) -> bool:
+ for text in indicators or config.LOGGED_IN_INDICATORS:
if page.get_by_text(text, exact=False).count() > 0:
return True
return False
-def wait_for_login(page: Page, timeout_s: int = 180, log: Callable[[str], None] = print) -> None:
- """Väntar tills sidan visar inloggad status (se config.LOGGED_IN_INDICATORS).
+def wait_for_login(
+ page: Page,
+ timeout_s: int = 180,
+ log: Callable[[str], None] = print,
+ indicators: Optional[Sequence[str]] = None,
+) -> None:
+ """Väntar tills sidan visar inloggad status.
+
+ Vilken text som räknas som "inloggad" avgörs av `indicators` om det
+ anges, annars faller det tillbaka på config.LOGGED_IN_INDICATORS. I
+ webui:t sätts detta per flöde/steg (steget "Vänta på inloggning") så
+ olika sajter kan ha olika text utan att röra config.py.
Om redan inloggad returnerar direkt. Annars pollar tills tidsgränsen
slår till, medan du godkänner BankID manuellt i telefonen.
"""
- if is_logged_in(page):
+ if is_logged_in(page, indicators):
log("Redan inloggad (återanvänd session).")
return
@@ -31,7 +41,7 @@ def wait_for_login(page: Page, timeout_s: int = 180, log: Callable[[str], None]
poll_interval_s = 2
waited = 0
while waited < timeout_s:
- if is_logged_in(page):
+ if is_logged_in(page, indicators):
log("Inloggning klar.")
return
page.wait_for_timeout(poll_interval_s * 1000)
@@ -39,7 +49,8 @@ def wait_for_login(page: Page, timeout_s: int = 180, log: Callable[[str], None]
raise TimeoutError(
"Ingen inloggning upptäcktes inom tidsgränsen. Höj timeout eller "
- "kontrollera config.LOGGED_IN_INDICATORS mot faktisk sidtext "
+ "kontrollera indikatortexterna (steget \"Vänta på inloggning\" i "
+ "flödet, annars config.LOGGED_IN_INDICATORS) mot faktisk sidtext "
"(kör python -m rpa.discover)."
)
diff --git a/rpa/codegen.py b/rpa/codegen.py
index 9a6f9a2..81f91df 100644
--- a/rpa/codegen.py
+++ b/rpa/codegen.py
@@ -11,7 +11,7 @@ en process per profilmapp - stäng ev. körande flöde i webui:t först.
Användning:
python -m rpa.codegen # startar på config.START_URL
- python -m rpa.codegen https://www.1177.se/sida # startar på angiven URL
+ python -m rpa.codegen https://www.sida.se/sida # startar på angiven URL
"""
import subprocess
diff --git a/rpa/config.py b/rpa/config.py
index dad26b7..600c22d 100644
--- a/rpa/config.py
+++ b/rpa/config.py
@@ -11,19 +11,11 @@ TRACE_DIR = DATA_DIR / "traces"
EXPORT_DIR = DATA_DIR / "exports"
STATE_FILE = DATA_DIR / "state.json"
-START_URL = "https://www.1177.se/"
+START_URL = "https://www.changeme.com/"
-# Text som skiftlägesokänsligt matchas mot länkar/rubriker för att hitta
-# rätt sida. Fyll på/justera efter vad --discover visar på den riktiga sidan.
-REMISS_LINK_TEXT_CANDIDATES = [
- "Egenremiss",
- "Egenremisser",
- "Mina remisser",
- "Skickade remisser",
- "Remisser",
-]
-
-# Text som indikerar att inloggning lyckats (letas efter i sidans innehåll).
+# Standardtext som indikerar att inloggning lyckats (letas efter i sidans
+# innehåll). Kan överstyras per flöde/steg i webui:t (steget "Vänta på
+# inloggning"); används annars av rpa/auth.py.
LOGGED_IN_INDICATORS = [
"Logga ut",
"Mina vårdkontakter",
diff --git a/rpa/discover.py b/rpa/discover.py
index 6b1de2b..e4b9797 100644
--- a/rpa/discover.py
+++ b/rpa/discover.py
@@ -1,11 +1,11 @@
"""Hjälpverktyg för att inspektera den inloggade sidans faktiska struktur.
-Körs separat (python -m rpa.discover) eftersom selectors i scraper.py och
-config.py bygger på gissad textstruktur. Detta skript loggar in (manuellt,
+Körs separat (python -m rpa.discover) eftersom selectors i scraper.py
+bygger på gissad textstruktur. Detta skript loggar in (manuellt,
BankID) och låter dig sedan, för varje sida du manuellt navigerar till,
trycka Enter i terminalen för att spara en HTML-dump + skärmdump av aktuell
sida i data/discover/. Använd dumparna för att hitta exakt länktext/selectors
-och uppdatera config.REMISS_LINK_TEXT_CANDIDATES samt scraper.list_egenremisser.
+och uppdatera scraper.list_egenremisser.
"""
from datetime import datetime
diff --git a/rpa/flow_engine.py b/rpa/flow_engine.py
index c42dc59..1d7aa5a 100644
--- a/rpa/flow_engine.py
+++ b/rpa/flow_engine.py
@@ -182,7 +182,9 @@ def _run_steps(
elif t == "wait_for_login":
timeout_s = int(step.get("timeout", 180))
- wait_for_login(page, timeout_s, log=log)
+ raw_indicators = step.get("indicators") or ""
+ indicators = [s.strip() for s in raw_indicators.split(",") if s.strip()] or None
+ wait_for_login(page, timeout_s, log=log, indicators=indicators)
elif t == "wait_for_selector":
loc = _resolve_locator(page, item_locator, step)
diff --git a/rpa/notify.py b/rpa/notify.py
index 51cedcd..8b1a09a 100644
--- a/rpa/notify.py
+++ b/rpa/notify.py
@@ -1,10 +1,10 @@
def notify_new_items(items: list[dict]) -> None:
if not items:
- print("Inga nya eller ändrade egenremisser.")
+ print("Inga nya eller ändrade i databas.")
return
- title = "Egenremiss - uppdatering"
- message = f"{len(items)} ny(a)/ändrad(e) egenremiss(er) hittades."
+ title = "databass - uppdatering"
+ message = f"{len(items)} ny(a)/ändrad(e) post(er) hittades."
print(f"{title}: {message}")
for item in items:
preview = item["text"].splitlines()[0][:120]
diff --git a/rpa/scraper.py b/rpa/scraper.py
index 921fafb..a0bcf75 100644
--- a/rpa/scraper.py
+++ b/rpa/scraper.py
@@ -5,25 +5,6 @@ from typing import Optional
from playwright.sync_api import Page
-from . import config
-
-
-def goto_egenremisser(page: Page) -> bool:
- """Försöker klicka sig fram till egenremiss-sidan via länktext.
-
- Returnerar True om en matchande länk hittades och klickades.
- Om detta misslyckas: kör discover-läget, hitta den riktiga länken/
- rubriken i dumpen, och lägg till exakt text i
- config.REMISS_LINK_TEXT_CANDIDATES.
- """
- for text in config.REMISS_LINK_TEXT_CANDIDATES:
- link = page.get_by_role("link", name=re.compile(text, re.I))
- if link.count() > 0:
- link.first.click()
- page.wait_for_load_state("domcontentloaded")
- return True
- return False
-
def _stable_id(*parts: str) -> str:
raw = "|".join(p.strip() for p in parts if p)
diff --git a/webui/templates/index.html b/webui/templates/index.html
index 7a39cff..e0b1433 100644
--- a/webui/templates/index.html
+++ b/webui/templates/index.html
@@ -347,6 +347,7 @@ const I18N = {
fd_goto_url: "URL",
fd_wait_seconds: "Seconds",
fd_timeout_seconds: "Timeout (seconds)",
+ fd_login_indicators: "Logged-in text (comma-separated, empty = default)",
fd_type_text: "Text to type",
fd_press_key_key: "Key (e.g. Enter, Tab, Escape)",
fd_press_key_value: "Text / CSS selector / role name (empty = whole page)",
@@ -454,6 +455,7 @@ const I18N = {
fd_goto_url: "URL",
fd_wait_seconds: "Sekunder",
fd_timeout_seconds: "Timeout (sekunder)",
+ fd_login_indicators: "Text som visar inloggad (komma-separerad, tomt = standard)",
fd_type_text: "Text att skriva",
fd_press_key_key: "Tangent (ex. Enter, Tab, Escape)",
fd_press_key_value: "Text / CSS-selector / rollnamn (tomt = hela sidan)",
@@ -533,7 +535,10 @@ const FIELD_DEFS = {
goto: [{ key: "url", labelKey: "fd_goto_url", type: "text" }],
wait: [{ key: "seconds", labelKey: "fd_wait_seconds", type: "number", default: 1 }],
wait_for_load: [],
- wait_for_login: [{ key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 180 }],
+ wait_for_login: [
+ { key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 180 },
+ { key: "indicators", labelKey: "fd_login_indicators", type: "text" },
+ ],
wait_for_selector: [
...SELECTOR_FIELDS,
{ key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 15 },