foxygit / RPA-Remisser Log in
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 },