Add Outlook/Soubory/Clario/Feasibility scripts and reports; ignore Incoming, Outlook downloads & profile
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# JustOpenOutlook_v1.0
|
||||
|
||||
**Verze:** 1.0
|
||||
**Datum:** 2026-06-03
|
||||
|
||||
## Cíl
|
||||
Jen otevře Outlook OWA v Playwrightu pomocí už uloženého persistent profilu —
|
||||
žádný login, žádné ukládání.
|
||||
|
||||
## Co dělá
|
||||
1. Načte profil `outlook_profile/` (vytvořený `outlook_login_v1.0.py`).
|
||||
2. Otevře `https://outlook.cloud.microsoft/mail/`.
|
||||
3. Čeká na Enter v konzoli.
|
||||
4. Zavře prohlížeč.
|
||||
|
||||
## Spuštění
|
||||
```
|
||||
python JustOpenOutlook_v1.0.py
|
||||
```
|
||||
|
||||
## Předpoklad
|
||||
Existuje `outlook_profile/` ve stejném adresáři.
|
||||
Pokud ne — nejprve spustit `outlook_login_v1.0.py`.
|
||||
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
=======================================================================
|
||||
Název: JustOpenOutlook_v1.0.py
|
||||
Verze: 1.0
|
||||
Datum: 2026-06-03
|
||||
Popis: Otevře Outlook OWA v persistent Chromium profilu vytvořeném
|
||||
skriptem outlook_login_v1.0.py. Žádný login — pouze otevře
|
||||
okno, počká, až uživatel stiskne Enter, a zavře.
|
||||
=======================================================================
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
PROFILE_DIR = BASE_DIR / "outlook_profile"
|
||||
START_URL = "https://outlook.cloud.microsoft/mail/"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not PROFILE_DIR.exists():
|
||||
print(f" Profil nenalezen: {PROFILE_DIR}")
|
||||
print(" Nejprve spusť outlook_login_v1.0.py a přihlas se.")
|
||||
return
|
||||
|
||||
with sync_playwright() as p:
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir=str(PROFILE_DIR),
|
||||
headless=False,
|
||||
no_viewport=True,
|
||||
args=[
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--start-maximized",
|
||||
],
|
||||
)
|
||||
|
||||
page = context.pages[0] if context.pages else context.new_page()
|
||||
page.goto(START_URL)
|
||||
|
||||
print()
|
||||
print("=" * 70)
|
||||
print(" Outlook otevřen. Stiskni Enter pro zavření.")
|
||||
print("=" * 70)
|
||||
input()
|
||||
|
||||
context.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,54 @@
|
||||
# download_all_inbox_eml_v1.0
|
||||
|
||||
**Verze:** 1.0
|
||||
**Datum:** 2026-06-03
|
||||
|
||||
## Cíl
|
||||
Stáhnout zprávy z Outlook Inboxu jako `.eml` soubory.
|
||||
|
||||
## Klíčový princip — virtualizovaný seznam
|
||||
OWA drží v DOM jen ~16 viditelných řádků. `nth(19)` proto nefunguje.
|
||||
Řešení: **navigace klávesnicí** — vybrat první zprávu a opakovaně mačkat
|
||||
`ArrowDown`. Outlook sám scrolluje a dorenderovává. Aktuálně vybraná zpráva
|
||||
je vždy `[role="option"][aria-selected="true"]`.
|
||||
|
||||
### Oddělovače sekcí (Today / Yesterday / This week)
|
||||
Jsou to `role="button"` `aria-expanded` prvky, ne zprávy. Když na nich kurzor
|
||||
po `ArrowDown` zastaví, **žádná** zpráva nemá `aria-selected`
|
||||
(`selected.count() == 0`). Takový krok se musí jen přeskočit (`ArrowDown` dál),
|
||||
NEpočítat jako zprávu a NEukončovat smyčku. Konec seznamu se pozná až podle
|
||||
toho, že se `aria-label` vybrané zprávy přestane měnit (`no_progress`).
|
||||
|
||||
Alternativa: v OWA přepnout řazení na "Show as Messages" (bez seskupení podle
|
||||
data) — pak seznam žádné oddělovače nemá.
|
||||
|
||||
## Postup
|
||||
1. Otevře OWA z persistent profilu (`outlook_profile/`).
|
||||
2. Přejde do Inboxu.
|
||||
3. Vybere první zprávu.
|
||||
4. Smyčka: stáhne vybranou (pravý klik → Download → Download as EML) →
|
||||
`ArrowDown` → opakuje, dokud se výběr přestane hýbat (= konec seznamu).
|
||||
|
||||
## Nastavení (v hlavičce skriptu)
|
||||
- `LIMIT` — max počet **uložených zpráv** (`None` = celý Inbox). Aktuálně `30`.
|
||||
- `SKIP_EXISTING` — `True` = soubor stejného jména v `downloads/` znovu neuloží;
|
||||
`False` (aktuální) = existující soubor **smaže a uloží nový** (přepis).
|
||||
|
||||
## Výstup
|
||||
`downloads/<název_z_OWA>.eml`. Při kolizi jmen:
|
||||
- `SKIP_EXISTING=False` → starý soubor se smaže a přepíše novým,
|
||||
- `SKIP_EXISTING=True` → soubor se ponechá, nový se neuloží.
|
||||
|
||||
## Spuštění
|
||||
```
|
||||
python download_all_inbox_eml_v1.0.py
|
||||
```
|
||||
|
||||
## Poznámky / omezení
|
||||
- Celý Inbox (tisíce zpráv) přes UI je pomalý a křehký — pro velký objem
|
||||
nejdřív zúžit hledáním/filtrem v OWA. `LIMIT=30` je rozumný test.
|
||||
- `SKIP_EXISTING` nešetří čas: identitu zprávy známe až z názvu **po** stažení,
|
||||
takže pravý klik + download proběhne pro každou zprávu; jen se nepřepíše soubor.
|
||||
- Konec seznamu se pozná tak, že se `aria-label` vybrané zprávy přestane měnit
|
||||
(počítadlo `no_progress`, práh `NO_PROGRESS_MAX = 4`).
|
||||
- Okno se po doběhnutí nezavře, čeká na Enter.
|
||||
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
=======================================================================
|
||||
Název: download_all_inbox_eml_v1.0.py
|
||||
Verze: 1.0
|
||||
Datum: 2026-06-03
|
||||
Popis: Stáhne zprávy z Outlook Inboxu jako .eml. Virtualizovaný seznam
|
||||
řeší navigací klávesnicí (ArrowDown) — Outlook sám scrolluje
|
||||
a dorenderovává. Postup:
|
||||
1. vybrat první zprávu
|
||||
2. stáhnout vybranou (pravý klik → Download → Download as EML)
|
||||
3. ArrowDown na další
|
||||
4. opakovat, dokud se výběr (aria-selected) přestane hýbat
|
||||
|
||||
Používá persistent profil z outlook_login_v1.0.py.
|
||||
|
||||
Nastavení:
|
||||
LIMIT – max počet zpráv (None = celý Inbox)
|
||||
SKIP_EXISTING – přeskočit zprávy, jejichž EML už v downloads/ existuje
|
||||
=======================================================================
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
PROFILE_DIR = BASE_DIR / "outlook_profile"
|
||||
OUT_DIR = BASE_DIR / "downloads"
|
||||
START_URL = "https://outlook.cloud.microsoft/mail/"
|
||||
|
||||
LIMIT = 30 # max počet zpráv; None = celý Inbox
|
||||
SKIP_EXISTING = False # False = existující stejný soubor přepsat (smazat + uložit nový)
|
||||
|
||||
|
||||
def safe_name(name: str) -> str:
|
||||
"""Očistí název pro filesystem (Windows)."""
|
||||
name = re.sub(r'[<>:"/\\|?*\r\n\t]', "_", name).strip().strip(".")
|
||||
return name[:150] or "message"
|
||||
|
||||
|
||||
def download_selected(page, out_dir: Path) -> Path | None:
|
||||
"""Pravý klik na vybranou zprávu → Download as EML. Vrátí cestu nebo None."""
|
||||
selected = page.locator('[role="option"][aria-selected="true"]').first
|
||||
if selected.count() == 0:
|
||||
return None
|
||||
|
||||
selected.click(button="right")
|
||||
page.wait_for_timeout(600)
|
||||
|
||||
# Download (rodič submenu)
|
||||
download_parent = None
|
||||
for name in ("Download", "Stáhnout"):
|
||||
loc = page.get_by_role("menuitem", name=name).first
|
||||
if loc.count() and loc.is_visible():
|
||||
download_parent = loc
|
||||
break
|
||||
if download_parent is None:
|
||||
page.keyboard.press("Escape")
|
||||
return None
|
||||
|
||||
download_parent.hover()
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
# Download as EML (submenu); fallback = klik přímo na Download
|
||||
eml_item = None
|
||||
for name in ("Download as EML", "Stáhnout jako EML", "Stáhnout jako .eml"):
|
||||
loc = page.get_by_role("menuitem", name=name).first
|
||||
if loc.count() and loc.is_visible():
|
||||
eml_item = loc
|
||||
break
|
||||
|
||||
try:
|
||||
if eml_item is not None:
|
||||
with page.expect_download(timeout=15_000) as dl:
|
||||
eml_item.click()
|
||||
else:
|
||||
with page.expect_download(timeout=15_000) as dl:
|
||||
download_parent.click()
|
||||
download = dl.value
|
||||
except Exception:
|
||||
page.keyboard.press("Escape")
|
||||
return None
|
||||
|
||||
fname = safe_name(download.suggested_filename or "message.eml")
|
||||
if not fname.lower().endswith(".eml"):
|
||||
fname += ".eml"
|
||||
target = out_dir / fname
|
||||
|
||||
if target.exists():
|
||||
if SKIP_EXISTING:
|
||||
return target # už máme — neukládat znovu
|
||||
target.unlink() # přepsat: smazat starou verzi a uložit novou
|
||||
|
||||
download.save_as(str(target))
|
||||
return target
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not PROFILE_DIR.exists():
|
||||
print(f" Profil nenalezen: {PROFILE_DIR}")
|
||||
print(" Nejprve spusť outlook_login_v1.0.py.")
|
||||
return
|
||||
OUT_DIR.mkdir(exist_ok=True)
|
||||
|
||||
with sync_playwright() as p:
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir=str(PROFILE_DIR),
|
||||
headless=False,
|
||||
no_viewport=True,
|
||||
accept_downloads=True,
|
||||
args=[
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--start-maximized",
|
||||
],
|
||||
)
|
||||
page = context.pages[0] if context.pages else context.new_page()
|
||||
|
||||
# 1) Otevřít Outlook
|
||||
print(" 1/4 Otevírám Outlook...")
|
||||
page.goto(START_URL)
|
||||
page.wait_for_load_state("domcontentloaded")
|
||||
search_selector = (
|
||||
'[placeholder*="Search"], [aria-label*="Search"], '
|
||||
'[placeholder*="Hledat"], [aria-label*="Hledat"]'
|
||||
)
|
||||
page.wait_for_selector(search_selector, timeout=30_000)
|
||||
|
||||
# 2) Inbox / Doručená pošta
|
||||
print(" 2/4 Otevírám Inbox...")
|
||||
inbox_candidates = [
|
||||
'div[role="treeitem"]:has-text("Inbox")',
|
||||
'div[role="treeitem"]:has-text("Doručená pošta")',
|
||||
'text=Inbox',
|
||||
'text=Doručená pošta',
|
||||
]
|
||||
for sel in inbox_candidates:
|
||||
loc = page.locator(sel).first
|
||||
if loc.count() and loc.is_visible():
|
||||
loc.click()
|
||||
break
|
||||
page.wait_for_selector('div[role="option"]', timeout=15_000)
|
||||
page.wait_for_timeout(1000)
|
||||
|
||||
# 3) Vybrat první zprávu
|
||||
print(" 3/4 Vybírám první zprávu...")
|
||||
page.locator('div[role="option"]').first.click()
|
||||
page.wait_for_timeout(800)
|
||||
|
||||
# 4) Smyčka: stáhni vybranou → ArrowDown → dokud se výběr hýbe
|
||||
# Pozn.: oddělovače sekcí (Today/Yesterday/...) jsou role="button"
|
||||
# aria-expanded — kurzor na nich ZASTAVÍ a žádná zpráva nemá
|
||||
# aria-selected (selected.count()==0). Takový krok jen přeskočíme
|
||||
# (ArrowDown dál), NEpočítáme ho a NEukončujeme smyčku.
|
||||
print(" 4/4 Stahuji zprávy...\n")
|
||||
saved = 0
|
||||
dividers = 0
|
||||
failed = 0
|
||||
prev_label = None
|
||||
no_progress = 0 # kolikrát po sobě se výběr neposunul
|
||||
NO_PROGRESS_MAX = 4 # tolik = konec seznamu / zaseknutí
|
||||
|
||||
while LIMIT is None or saved < LIMIT:
|
||||
selected = page.locator('[role="option"][aria-selected="true"]').first
|
||||
|
||||
# (a) stojíme na oddělovači sekce → krok přes něj
|
||||
if selected.count() == 0:
|
||||
dividers += 1
|
||||
no_progress += 1
|
||||
if no_progress >= NO_PROGRESS_MAX:
|
||||
print(" Konec seznamu / zaseknutí — končím.")
|
||||
break
|
||||
page.keyboard.press("ArrowDown")
|
||||
page.wait_for_timeout(250)
|
||||
continue
|
||||
|
||||
label = selected.get_attribute("aria-label") or ""
|
||||
|
||||
# (b) výběr se neposunul (konec seznamu)
|
||||
if label == prev_label:
|
||||
no_progress += 1
|
||||
if no_progress >= NO_PROGRESS_MAX:
|
||||
print(" Konec seznamu (výběr se nehýbe).")
|
||||
break
|
||||
page.keyboard.press("ArrowDown")
|
||||
page.wait_for_timeout(250)
|
||||
continue
|
||||
|
||||
# (c) nová zpráva → stáhni
|
||||
no_progress = 0
|
||||
prev_label = label
|
||||
target = download_selected(page, OUT_DIR)
|
||||
|
||||
if target is None:
|
||||
failed += 1
|
||||
print(f" [!] selhalo: {label[:70]}")
|
||||
else:
|
||||
saved += 1
|
||||
print(f" [{saved:>4}] {target.name}")
|
||||
|
||||
# refokus seznamu (klik na zprávu, ne na oddělovač) + posun dál
|
||||
try:
|
||||
selected.click()
|
||||
except Exception:
|
||||
pass
|
||||
page.wait_for_timeout(200)
|
||||
page.keyboard.press("ArrowDown")
|
||||
page.wait_for_timeout(300)
|
||||
|
||||
print(f"\n Hotovo. Uloženo {saved}, oddělovačů přeskočeno {dividers}, "
|
||||
f"selhalo {failed} → {OUT_DIR}")
|
||||
input(" Stiskni Enter pro zavření okna... ")
|
||||
context.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,30 @@
|
||||
# download_first_inbox_eml_v1.0
|
||||
|
||||
**Verze:** 1.0
|
||||
**Datum:** 2026-06-03
|
||||
|
||||
## Cíl
|
||||
Otevřít Outlook OWA, vybrat první zprávu v Inboxu a stáhnout ji jako `.eml`.
|
||||
|
||||
## Kroky
|
||||
1. Otevře OWA z persistent profilu (`outlook_profile/`).
|
||||
2. Přejde do Inboxu / Doručené pošty.
|
||||
3. Klikne na první zprávu v seznamu.
|
||||
4. **Pravý klik** na řádek zprávy → kontextové menu (patří celé zprávě, ne příloze)
|
||||
→ hover na **Download** → klik **Download as EML**, soubor uloží do `downloads/`.
|
||||
|
||||
## Výstup
|
||||
`downloads/<původní_název_z_OWA>.eml`
|
||||
|
||||
## Spuštění
|
||||
```
|
||||
python download_first_inbox_eml_v1.0.py
|
||||
```
|
||||
|
||||
## Poznámky
|
||||
- **Pravý klik na řádek zprávy** je spolehlivější než "..." v toolbaru — kontextové
|
||||
menu je vždy svázané s celou zprávou, takže odpadá riziko trefení "..." přílohy.
|
||||
- Na **Download** se najíždí `hover()` (otevře submenu), ne klikem.
|
||||
- Selektory mají EN i CZ varianty.
|
||||
- `accept_downloads=True` + `page.expect_download()` — bez toho Playwright stažení nezachytí.
|
||||
- Okno se po stažení nezavře, čeká na Enter.
|
||||
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
=======================================================================
|
||||
Název: download_first_inbox_eml_v1.0.py
|
||||
Verze: 1.0
|
||||
Datum: 2026-06-03
|
||||
Popis: Pokusný skript: otevře Outlook OWA, přejde do Inboxu, klikne
|
||||
na první zprávu a stáhne ji jako .eml přes menu
|
||||
"More email actions" → Download → Download as EML.
|
||||
|
||||
Používá persistent profil z outlook_login_v1.0.py.
|
||||
=======================================================================
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
PROFILE_DIR = BASE_DIR / "outlook_profile"
|
||||
OUT_DIR = BASE_DIR / "downloads"
|
||||
START_URL = "https://outlook.cloud.microsoft/mail/"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not PROFILE_DIR.exists():
|
||||
print(f" Profil nenalezen: {PROFILE_DIR}")
|
||||
print(" Nejprve spusť outlook_login_v1.0.py.")
|
||||
return
|
||||
OUT_DIR.mkdir(exist_ok=True)
|
||||
|
||||
with sync_playwright() as p:
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir=str(PROFILE_DIR),
|
||||
headless=False,
|
||||
no_viewport=True,
|
||||
accept_downloads=True,
|
||||
args=[
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--start-maximized",
|
||||
],
|
||||
)
|
||||
page = context.pages[0] if context.pages else context.new_page()
|
||||
|
||||
# 1) Otevřít Outlook
|
||||
print(" 1/6 Otevírám Outlook...")
|
||||
page.goto(START_URL)
|
||||
page.wait_for_load_state("domcontentloaded")
|
||||
search_selector = (
|
||||
'[placeholder*="Search"], [aria-label*="Search"], '
|
||||
'[placeholder*="Hledat"], [aria-label*="Hledat"]'
|
||||
)
|
||||
page.wait_for_selector(search_selector, timeout=30_000)
|
||||
|
||||
# 2) Inbox / Doručená pošta
|
||||
print(" 2/6 Otevírám Inbox...")
|
||||
inbox_candidates = [
|
||||
'div[role="treeitem"]:has-text("Inbox")',
|
||||
'div[role="treeitem"]:has-text("Doručená pošta")',
|
||||
'text=Inbox',
|
||||
'text=Doručená pošta',
|
||||
]
|
||||
for sel in inbox_candidates:
|
||||
loc = page.locator(sel).first
|
||||
if loc.count() and loc.is_visible():
|
||||
loc.click()
|
||||
break
|
||||
page.wait_for_selector('div[role="option"]', timeout=15_000)
|
||||
page.wait_for_timeout(1000)
|
||||
|
||||
# 3) První zpráva v inboxu
|
||||
print(" 3/4 Vybírám první zprávu...")
|
||||
first_msg = page.locator('div[role="option"]').first
|
||||
first_msg.click()
|
||||
page.wait_for_timeout(1000)
|
||||
|
||||
# 4) PRAVÝ KLIK na řádek zprávy → kontextové menu patří CELÉ zprávě
|
||||
# (ne příloze). Na "Download" najet hoverem (otevře submenu), pak
|
||||
# kliknout na "Download as EML".
|
||||
print(" 4/4 Pravý klik → Download → Download as EML...")
|
||||
first_msg.click(button="right")
|
||||
page.wait_for_timeout(700)
|
||||
|
||||
download_parent = None
|
||||
for name in ("Download", "Stáhnout"):
|
||||
loc = page.get_by_role("menuitem", name=name).first
|
||||
if loc.count() and loc.is_visible():
|
||||
download_parent = loc
|
||||
break
|
||||
if download_parent is None:
|
||||
items = page.get_by_role("menuitem").all()
|
||||
print(" ! Download položka v menu nenalezena. Obsah menu:")
|
||||
for it in items:
|
||||
try:
|
||||
txt = it.inner_text(timeout=500).strip().replace("\n", " | ")
|
||||
print(f" - {txt[:100]}")
|
||||
except Exception:
|
||||
pass
|
||||
page.screenshot(path=str(OUT_DIR / "debug_menu.png"))
|
||||
print(f" screenshot: {OUT_DIR / 'debug_menu.png'}")
|
||||
input(" Enter pro zavření... ")
|
||||
context.close()
|
||||
return
|
||||
|
||||
download_parent.hover()
|
||||
page.wait_for_timeout(600)
|
||||
|
||||
eml_item = None
|
||||
for name in ("Download as EML", "Stáhnout jako EML", "Stáhnout jako .eml"):
|
||||
loc = page.get_by_role("menuitem", name=name).first
|
||||
if loc.count() and loc.is_visible():
|
||||
eml_item = loc
|
||||
break
|
||||
|
||||
try:
|
||||
if eml_item is not None:
|
||||
with page.expect_download(timeout=15_000) as download_info:
|
||||
eml_item.click()
|
||||
else:
|
||||
# některé buildy OWA stahují EML přímo bez submenu
|
||||
with page.expect_download(timeout=15_000) as download_info:
|
||||
download_parent.click()
|
||||
download = download_info.value
|
||||
except Exception as e:
|
||||
page.screenshot(path=str(OUT_DIR / "debug_menu.png"))
|
||||
print(f" ! Stažení selhalo: {e}")
|
||||
print(f" screenshot: {OUT_DIR / 'debug_menu.png'}")
|
||||
input(" Enter pro zavření... ")
|
||||
context.close()
|
||||
return
|
||||
|
||||
target = OUT_DIR / (download.suggested_filename or "first_inbox.eml")
|
||||
download.save_as(str(target))
|
||||
print(f" Hotovo → {target}")
|
||||
|
||||
if not target.name.lower().endswith(".eml"):
|
||||
print(f" ! POZOR: {target.name} nevypadá jako EML — možná stažena příloha!")
|
||||
|
||||
input(" Stiskni Enter pro zavření okna... ")
|
||||
context.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,30 @@
|
||||
# forward_last_to_klucho_v1.0
|
||||
|
||||
**Verze:** 1.0
|
||||
**Datum:** 2026-06-03
|
||||
|
||||
## Cíl
|
||||
Pokusný skript: přepošle poslední odeslaný e-mail na `klucho@gastroenterolog.com`
|
||||
na adresu `vladimir.buzalka@buzalka.cz` s předmětem `Ahoj` a slovem `Ahoj`
|
||||
na prvním řádku těla.
|
||||
|
||||
## Kroky
|
||||
1. Otevře OWA (persistent profil z `outlook_login_v1.0.py`).
|
||||
2. Přejde do Odeslané pošty.
|
||||
3. Vyhledá `to:klucho@gastroenterolog.com`.
|
||||
4. Otevře nejnovější výsledek.
|
||||
5. Klikne Forward / Přeposlat.
|
||||
6. Vyplní příjemce.
|
||||
7. Změní předmět na `Ahoj`.
|
||||
8. Vloží `Ahoj` na první řádek těla.
|
||||
9. Odešle (a počká na potvrzení Enterem před zavřením okna).
|
||||
|
||||
## Poznámky
|
||||
- Selektory mají EN i CZ varianty (`Forward` / `Přeposlat`, `To` / `Komu`, …).
|
||||
- `headless=False` — schválně viditelné, aby šlo sledovat průběh.
|
||||
- POZOR: krok 9 reálně odešle e-mail. Pro suchý běh zakomentuj `send_btn.click()`.
|
||||
|
||||
## Spuštění
|
||||
```
|
||||
python forward_last_to_klucho_v1.0.py
|
||||
```
|
||||
@@ -0,0 +1,155 @@
|
||||
"""
|
||||
=======================================================================
|
||||
Název: forward_last_to_klucho_v1.0.py
|
||||
Verze: 1.0
|
||||
Datum: 2026-06-03
|
||||
Popis: Pokusný skript: v Outlook OWA najde poslední odeslaný e-mail
|
||||
na adresu klucho@gastroenterolog.com, otevře Forward, vyplní
|
||||
příjemce vladimir.buzalka@buzalka.cz, předmět "Ahoj", na
|
||||
první řádek těla "Ahoj" a odešle.
|
||||
|
||||
Používá persistent profil z outlook_login_v1.0.py.
|
||||
headless=False kvůli sledování průběhu.
|
||||
=======================================================================
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
PROFILE_DIR = BASE_DIR / "outlook_profile"
|
||||
START_URL = "https://outlook.cloud.microsoft/mail/"
|
||||
|
||||
TARGET_RECIPIENT = "klucho@gastroenterolog.com"
|
||||
FORWARD_TO = "vladimir.buzalka@buzalka.cz"
|
||||
GREETING = "Ahoj"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not PROFILE_DIR.exists():
|
||||
print(f" Profil nenalezen: {PROFILE_DIR}")
|
||||
print(" Nejprve spusť outlook_login_v1.0.py.")
|
||||
return
|
||||
|
||||
with sync_playwright() as p:
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir=str(PROFILE_DIR),
|
||||
headless=False,
|
||||
no_viewport=True,
|
||||
args=[
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--start-maximized",
|
||||
],
|
||||
)
|
||||
page = context.pages[0] if context.pages else context.new_page()
|
||||
|
||||
# 1) Otevřít Outlook
|
||||
print(" 1/9 Otevírám Outlook...")
|
||||
page.goto(START_URL)
|
||||
page.wait_for_load_state("domcontentloaded")
|
||||
# Search box má proměnlivý placeholder; zkusíme víc variant
|
||||
search_selector = (
|
||||
'[placeholder*="Search"], [aria-label*="Search"], '
|
||||
'[placeholder*="Hledat"], [aria-label*="Hledat"]'
|
||||
)
|
||||
page.wait_for_selector(search_selector, timeout=30_000)
|
||||
|
||||
# 2) Přejít do Sent Items / Odeslaná pošta
|
||||
print(" 2/9 Otevírám Odeslanou poštu...")
|
||||
sent_candidates = [
|
||||
'div[role="treeitem"]:has-text("Sent Items")',
|
||||
'div[role="treeitem"]:has-text("Odeslaná pošta")',
|
||||
'text=Sent Items',
|
||||
'text=Odeslaná pošta',
|
||||
]
|
||||
for sel in sent_candidates:
|
||||
loc = page.locator(sel).first
|
||||
if loc.count() and loc.is_visible():
|
||||
loc.click()
|
||||
break
|
||||
page.wait_for_timeout(1500)
|
||||
|
||||
# 3) Vyhledat e-maily na příjemce
|
||||
print(f" 3/9 Hledám e-maily na {TARGET_RECIPIENT}...")
|
||||
search = page.locator(search_selector).first
|
||||
search.click()
|
||||
search.fill(f"to:{TARGET_RECIPIENT}")
|
||||
search.press("Enter")
|
||||
page.wait_for_timeout(2500)
|
||||
|
||||
# 4) Kliknout na první (nejnovější) výsledek
|
||||
print(" 4/9 Otevírám nejnovější výsledek...")
|
||||
first_msg = page.locator('div[role="option"]').first
|
||||
first_msg.wait_for(state="visible", timeout=15_000)
|
||||
first_msg.click()
|
||||
page.wait_for_timeout(2000)
|
||||
|
||||
# 5) Forward
|
||||
print(" 5/9 Klikám Forward...")
|
||||
forward_candidates = [
|
||||
'button[aria-label="Forward"]',
|
||||
'button[aria-label="Přeposlat"]',
|
||||
'button:has-text("Forward")',
|
||||
'button:has-text("Přeposlat")',
|
||||
]
|
||||
clicked = False
|
||||
for sel in forward_candidates:
|
||||
btn = page.locator(sel).first
|
||||
if btn.count() and btn.is_visible():
|
||||
btn.click()
|
||||
clicked = True
|
||||
break
|
||||
if not clicked:
|
||||
print(" ! Tlačítko Forward nenalezeno — končím.")
|
||||
input(" Stiskni Enter pro zavření... ")
|
||||
context.close()
|
||||
return
|
||||
|
||||
# 6) Příjemce
|
||||
print(f" 6/9 Vyplňuji příjemce {FORWARD_TO}...")
|
||||
to_field = page.locator(
|
||||
'[aria-label="To"], [aria-label="Komu"], '
|
||||
'[placeholder*="To"], [placeholder*="Komu"]'
|
||||
).first
|
||||
to_field.wait_for(state="visible", timeout=10_000)
|
||||
to_field.click()
|
||||
to_field.fill(FORWARD_TO)
|
||||
page.keyboard.press("Tab")
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
# 7) Předmět
|
||||
print(f" 7/9 Měním předmět na '{GREETING}'...")
|
||||
subject = page.locator(
|
||||
'[aria-label="Subject"], [aria-label="Předmět"]'
|
||||
).first
|
||||
subject.click()
|
||||
# vybrat vše a přepsat
|
||||
page.keyboard.press("Control+A")
|
||||
page.keyboard.type(GREETING)
|
||||
|
||||
# 8) Tělo — "Ahoj" na první řádek
|
||||
print(f" 8/9 Vkládám '{GREETING}' na první řádek těla...")
|
||||
body = page.locator(
|
||||
'[aria-label="Message body"], [aria-label="Tělo zprávy"], '
|
||||
'div[role="textbox"][contenteditable="true"]'
|
||||
).first
|
||||
body.click()
|
||||
page.keyboard.press("Control+Home")
|
||||
page.keyboard.type(GREETING)
|
||||
page.keyboard.press("Enter")
|
||||
|
||||
# 9) Send — POZOR: skutečně odešle e-mail
|
||||
print(" 9/9 Odesílám...")
|
||||
send_btn = page.locator(
|
||||
'button[aria-label="Send"], button[aria-label="Odeslat"]'
|
||||
).first
|
||||
send_btn.click()
|
||||
page.wait_for_timeout(3000)
|
||||
|
||||
print(" Hotovo — e-mail odeslán.")
|
||||
input(" Stiskni Enter pro zavření okna... ")
|
||||
context.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,44 @@
|
||||
# outlook_login_v1.0
|
||||
|
||||
**Verze:** 1.0
|
||||
**Datum:** 2026-06-03
|
||||
|
||||
## Cíl
|
||||
Jednorázové ruční přihlášení do Outlook OWA (`https://outlook.cloud.microsoft/mail/`)
|
||||
a uložení session pro pozdější neinteraktivní skripty.
|
||||
|
||||
## Co dělá
|
||||
1. Spustí Chromium v **persistent contextu** (adresář `outlook_profile/` vedle skriptu).
|
||||
2. Otevře OWA.
|
||||
3. Čeká, až se uživatel ručně přihlásí (účet, heslo, MFA, "Stay signed in").
|
||||
4. V konzoli se zeptá `Hotovo? Napiš 'OK' pro uložení session:`.
|
||||
5. Po zadání `OK` uloží:
|
||||
- `outlook_profile/` — persistent profil (cookies, IndexedDB, service workers)
|
||||
- `outlook_auth.json` — `storage_state` (cookies + localStorage)
|
||||
6. Zavře prohlížeč.
|
||||
|
||||
## Spuštění
|
||||
```
|
||||
python outlook_login_v1.0.py
|
||||
```
|
||||
|
||||
## Závislosti
|
||||
- `playwright` (`pip install playwright && playwright install chromium`)
|
||||
|
||||
## Použití session v dalším skriptu
|
||||
Persistent profil (doporučeno pro OWA):
|
||||
```python
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir="./outlook_profile",
|
||||
headless=False,
|
||||
)
|
||||
```
|
||||
|
||||
Nebo `storage_state` (pokud chceš jen cookies):
|
||||
```python
|
||||
context = browser.new_context(storage_state="outlook_auth.json")
|
||||
```
|
||||
|
||||
## Poznámky
|
||||
- Při prvním přihlášení zaškrtnout **"Zůstat přihlášen"** — MFA cookie u J&J typicky vydrží ~30 dní.
|
||||
- Pokud session vyprší, stačí znovu spustit tento skript.
|
||||
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
=======================================================================
|
||||
Název: outlook_login_v1.0.py
|
||||
Verze: 1.0
|
||||
Datum: 2026-06-03
|
||||
Popis: Otevře Outlook OWA (https://outlook.cloud.microsoft/mail/)
|
||||
v persistent Chromium profilu, počká na ruční přihlášení
|
||||
uživatele (včetně MFA), po potvrzení v konzoli uloží
|
||||
session (profile + storage_state) a zavře prohlížeč.
|
||||
|
||||
Další skripty mohou stejný profil znovu otevřít bez loginu.
|
||||
=======================================================================
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
PROFILE_DIR = BASE_DIR / "outlook_profile"
|
||||
STORAGE_STATE = BASE_DIR / "outlook_auth.json"
|
||||
START_URL = "https://outlook.cloud.microsoft/mail/"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
PROFILE_DIR.mkdir(exist_ok=True)
|
||||
|
||||
with sync_playwright() as p:
|
||||
context = p.chromium.launch_persistent_context(
|
||||
user_data_dir=str(PROFILE_DIR),
|
||||
headless=False,
|
||||
no_viewport=True,
|
||||
args=[
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--start-maximized",
|
||||
],
|
||||
)
|
||||
|
||||
page = context.pages[0] if context.pages else context.new_page()
|
||||
page.goto(START_URL)
|
||||
|
||||
print()
|
||||
print("=" * 70)
|
||||
print(" Přihlas se v otevřeném okně do Outlooku.")
|
||||
print(" Až budeš v inboxu (vidíš seznam e-mailů), vrať se sem.")
|
||||
print("=" * 70)
|
||||
answer = input(" Hotovo? Napiš 'OK' pro uložení session: ").strip().lower()
|
||||
|
||||
if answer == "ok":
|
||||
try:
|
||||
context.storage_state(path=str(STORAGE_STATE))
|
||||
print(f" Uloženo: {STORAGE_STATE}")
|
||||
except Exception as e:
|
||||
print(f" storage_state se neuložil: {e}")
|
||||
print(f" Persistent profil: {PROFILE_DIR}")
|
||||
else:
|
||||
print(" Zrušeno — session se neuloží (profil ale zůstává).")
|
||||
|
||||
context.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user