Files
2026-06-04 07:15:17 +02:00

13 KiB

python-runner — Docker kontejner na Tower

Základní info

Parametr Hodnota
Název python-runner
Image python-runner (vlastní)
Status running (unless-stopped)
Python 3.12.13
Spouštěcí cmd tail -f /dev/null — container jen běží, skripty se spouštějí ručně
Working dir /scripts
Vytvořen 2026-06-02

Tower — SSH přístup

Parametr Hodnota
Host tower / 192.168.1.76
Port 22
User root
Heslo 7309208104

Připojení přes Python (paramiko) — Docker CLI není lokálně dostupný:

import paramiko
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect('192.168.1.76', username='root', password='7309208104')
_, out, _ = c.exec_command('...')
print(out.read().decode())
c.close()

Volume mounty

Host (Unraid) Kontejner Popis
/mnt/user/Scripts /scripts Skripty, logy — working dir
/mnt/user/Emails /mnt/Emails Stažené přílohy <schránka>/Attachments/

Skripty čtou emaily přímo přes Microsoft Graph API — lokální .msg soubory už nejsou potřeba.


Pipeline — 5 skriptů v pořadí spouštění

Prefix 1_5_ indikuje pořadí v pipeline. Bezpečné opakovat každý krok (idempotentní upserty).

# Skript Účel Zdroj → Cíl
1 1_parse_emails_graph_v1.4.py Import emailů z Graph API Graph → Mongo emaily.<mailbox>
2 2_refetch_text_bodies_v1.0.py ONETIME oprava starých plain-text emailů (v1.3 ukládal jen 2000 znaků do body_preview) Graph → Mongo body_text
3 3_download_attachments_v1.3.py Stažení binárek příloh + SHA256 dedup Graph → /mnt/Emails/<mailbox>/Attachments/ + Mongo attachments_index
4 4_unwrap_smime_v1.0.py Rozbalení S/MIME wrapper (smime.p7m) na vnitřní MIME tělo Graph → Mongo smime_body_text/html, smime_inner_attachments
5 5_enrich_fulltext_emails_v1.2.py Plný text emailů do PG fulltext indexu Mongo → PG MongoEmaily.emails

Doplňkové soubory v /scripts/:

Soubor Popis
python_runner.md Tato dokumentace
*.log Výstupy běhů (parse_emails.log, download_attachments.log, unwrap_smime.log, refetch.log)
*_errors.log Chyby konkrétních zpráv/příloh
Trash/ Staré verze skriptů

POZOR: všechny skripty pouze čtou ze schránky — žádný zápis do schránky.


Microsoft Graph API — konfigurace (sdílená všemi skripty)

Parametr Hodnota
Graph URL https://graph.microsoft.com/v1.0
Tenant ID 7d269944-37a4-43a1-8140-c7517dc426e9
Client ID 4b222bfd-78c9-4239-a53f-43006b3ed07f
Auth client credentials (msal)
MongoDB Hodnota
URI mongodb://192.168.1.76:27017
DB emaily
Kolekce emailů <mailbox> (např. ordinace@buzalkova.cz)
Index příloh attachments_index
PostgreSQL Hodnota
Host 192.168.1.76
DB MongoEmaily
Tabulka emails (GIN tsvector, config soubory)

1) 1_parse_emails_graph_v1.4.py — import emailů → MongoDB

Čte všechny složky schránky rekurzivně (Inbox, Sent, Deleted, archivy …) přes Graph API a importuje každou zprávu jako dokument do MongoDB. _id = Internet Message-ID (fallback graphid:<id>). Upsert → bezpečné přerušit a opakovat.

Z každé zprávy extrahuje: předmět, odesílatel, příjemci To/CC/BCC, časy (UTC), HTML tělo (max 2 MB) + text preview, plné plain-text tělo (body_text, max 2 MB), přílohy (metadata + graph_att_id), internet headers (SPF/DKIM/Received/X-*), MAPI-ekvivalenty (důležitost, příznak, konverzační vlákno, kategorie, In-Reply-To, References), isRead, isDraft, folder_path, inferenceClassification.

# První import (vše):
docker exec -it python-runner python /scripts/1_parse_emails_graph_v1.4.py --mailbox ordinace@buzalkova.cz

# Test na 50 zprávách bez indexů:
docker exec -it python-runner python /scripts/1_parse_emails_graph_v1.4.py --mailbox ordinace@buzalkova.cz --limit 50 --no-indexes

# Pravidelný sync na pozadí (log do souboru):
docker exec -d python-runner bash -c "python /scripts/1_parse_emails_graph_v1.4.py --mailbox ordinace@buzalkova.cz --mode sync > /scripts/parse_emails.log 2>&1"

-d = detached: příkaz se hned vrátí a skript běží dál v kontejneru i po zavření terminálu / odpojení SSH. Bez -d (resp. s -it) skript skončí ve chvíli, kdy se spojení zavře. Pro dlouhé běhy vždy pouštěj s -d a logem do souboru, průběh pak sleduj přes tail -f (viz Sledování průběhu).

Parametry

Parametr Popis
--mailbox Povinný. Schránka (e-mail), zároveň název kolekce v MongoDB.
--mode full (výchozí — plný upsert), new-only (jen nové), sync (existující: aktualizuje is_read/flag_status/categories/modified_at/folder_path; nové importuje celé — ideální pro pravidelné spouštění).
--folder Import jen jedné složky (např. Inbox).
--limit N Zpracuje jen prvních N zpráv (test).
--no-indexes Nevytváří indexy na konci.

2) 2_refetch_text_bodies_v1.0.py — dohnání plain-text těl

ONETIME oprava. Starý parse_emails_graph_v1.3 ukládal plain-text emaily jen jako prvních 2000 znaků do body_preview — plné tělo se zahazovalo. Tenhle skript v Mongo najde emaily kde body_html chybí a re-fetchne plné tělo z Graphu do nového pole body_text (max 2 MB).

docker exec -d python-runner bash -c "python /scripts/2_refetch_text_bodies_v1.0.py --mailbox ordinace@buzalkova.cz > /scripts/refetch.log 2>&1"

Po importu schránky přes v1.4 už tenhle skript prakticky nemá co dělat (kandidátů 0). Drží se kvůli archivním schránkám, které byly importovány v1.3.


3) 3_download_attachments_v1.3.py — stažení příloh → /mnt/Emails

Stahuje skutečné přílohy (is_inline=False) všech emailů z MongoDB přes Graph API do /mnt/Emails/<schránka>/Attachments/. Primárně přes graph_att_id (přímé ID), name-matching jako fallback pro staré emaily.

Deduplikace podle SHA256 obsahu:

  • stejný hash → soubor už existuje → přeskočí
  • kolize názvu (stejný název, jiný hash) → faktura_2.pdf, faktura_3.pdf

Po uložení aktualizuje MongoDB: každá příloha dostane file_hash + local_path; kolekce emaily.attachments_index (_id=hash, filename, path, size_bytes, mime_type, mailbox, first_seen_at, ref_count). Emaily kde mají všechny přílohy file_hash se přeskočí → bezpečné opakovat.

# Interaktivně:
docker exec -it python-runner python /scripts/3_download_attachments_v1.3.py --mailbox ordinace@buzalkova.cz

# Na pozadí:
docker exec -d python-runner bash -c "python /scripts/3_download_attachments_v1.3.py --mailbox ordinace@buzalkova.cz > /scripts/download_attachments.log 2>&1"

Parametry

Parametr Popis
--mailbox Povinný. Schránka (e-mail) = kolekce v MongoDB.
--limit N Zpracuje jen prvních N emailů (test).
--force-recheck Znovu ověří i už stažené přílohy.
--no-indexes Nevytváří indexy na konci.

4) 4_unwrap_smime_v1.0.py — rozbalení S/MIME zpráv

Některé emaily (Datová schránka, mBank, ComGate, PayU, PostSignum …) přicházejí jako S/MIME signed-data wrapper: viditelné tělo je jen "This is an S/MIME signed message", skutečný obsah je zabalený uvnitř přílohy smime.p7m.

Skript najde tyto emaily, stáhne binárku smime.p7m z Graphu, rozbalí PKCS7 SignedData (asn1crypto.cms), extrahuje vnitřní MIME zprávu a doplní do Mongo:

Pole Obsah
smime_unwrapped: True flag — už rozbaleno
smime_subject Subject z vnitřní MIME hlavičky
smime_body_text plain text vnitřního těla
smime_body_html HTML vnitřního těla (pokud je)
smime_inner_attachments[] {filename, content_type, size_bytes} vnitřních příloh

Pole pak používá 5_enrich_fulltext_emails_v1.2 — preferuje smime_body_* před prázdným wrapper tělem a názvy vnitřních příloh přidá do attachments_summary (takže je najde MCP emaily.find_attachment).

docker exec -it python-runner python /scripts/4_unwrap_smime_v1.0.py                              # vsechny schránky
docker exec -it python-runner python /scripts/4_unwrap_smime_v1.0.py --mailbox ordinace@buzalkova.cz
docker exec -it python-runner python /scripts/4_unwrap_smime_v1.0.py --limit 10                   # test

POZOR: smime.p7m vs smime.p7s — dva různé typy

Příloha Co to je Skript dělá
smime.p7m Enveloped/signed-data wrapper — vnější obal kolem celé MIME zprávy. Bez rozbalení je viditelné jen "This is an S/MIME signed message". Rozbalí → extrahuje vnitřní tělo + přílohy do Mongo.
smime.p7s Detached signature — jen digitální podpis vedle čistého emailu. Vlastní body_html / body_text je normálně dostupné. Ignoruje — není co rozbalovat. Mail je už čitelný.

Filtr ve skriptu (SMIME_FILTER) je proto explicitně ^smime\.p7m$. Pokud při auditu vidíš email s přílohou smime.p7s a smime_unwrapped != True, je to správně — žádná akce není potřeba.

Závislosti

pip install asn1crypto

5) 5_enrich_fulltext_emails_v1.2.py — fulltext do PostgreSQL

Vytáhne plný text z emailů v MongoDB a uloží do PostgreSQL (MongoEmaily.emails) s GIN tsvector indexem (config soubory — simple + unaccent). Emaily se nestahují znovu — tělo už je v Mongo z kroků 1/2/4.

Priorita zdroje těla (body_source):

  1. smimesmime_body_text / smime_body_html (pokud unwrap proběhl)
  2. htmlbody_html
  3. textbody_text (z parse v1.4 nebo refetch v1.0)
  4. previewbody_preview (fallback)

Inkrementalita: pokud (mailbox, message_id) existuje a extractor_version je aktuální a modified_at v Mongo není novější → skip. Bump EXTRACTOR_VERSION přeparsuje vše.

docker exec -d python-runner bash -c "python /scripts/5_enrich_fulltext_emails_v1.2.py > /scripts/enrich.log 2>&1"
docker exec -it python-runner python /scripts/5_enrich_fulltext_emails_v1.2.py --mailbox ordinace@buzalkova.cz
docker exec -it python-runner python /scripts/5_enrich_fulltext_emails_v1.2.py --limit 500   # test

Sledování průběhu

docker exec -it python-runner tail -f /scripts/parse_emails.log
docker exec -it python-runner tail -f /scripts/download_attachments.log
docker exec -it python-runner tail -f /scripts/unwrap_smime.log

Nainstalované Python balíčky

msal               (Graph API auth)
requests
pymongo            4.17.0
python-dateutil    2.9.0.post0
extract-msg        0.55.0
cryptography       48.0.0
asn1crypto         (S/MIME unwrap)
beautifulsoup4     4.13.5
oletools           0.60.2
msoffcrypto-tool   6.0.0
olefile            0.47
RTFDE              0.1.2.2
compressed-rtf     1.0.7
lark               1.3.1
pcodedmp           1.2.6
tzlocal            5.3.1
six                1.17.0
pip                25.0.1
psycopg            (PG klient pro krok 5)

Přidání nového balíčku

docker exec python-runner pip install <balicek>

Pozor: instalace se ztratí při recreate kontejneru — je třeba přidat do Dockerfile nebo do setup skriptu.


Historie

Datum Změna
2026-06-02 Přechod z .msg souborů na Microsoft Graph API. parse_emails_tower_v1.1.py (import lokálních .msg) nahrazen parse_emails_graph_v1.3.py; přidán download_attachments_v1.3.py. Staré verze v Trash/.
2026-06-03 parse_emails_graph_v1.4 (ukládá i plné plain-text tělo do body_text). Přidán refetch_text_bodies_v1.0 (dohnání starých plain-text). Přidán unwrap_smime_v1.0 (rozbalení smime.p7m). enrich_fulltext_emails_v1.2 (preferuje smime_body_*, body_source smime/text).
2026-06-04 Skripty přejmenovány s prefixem 1_…5_ podle pořadí v pipeline. enrich_v1.1 + parse_emails_tower_v1.1* do Trash/.