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í
.msgsoubory 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-da logem do souboru, průběh pak sleduj přestail -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):
smime—smime_body_text/smime_body_html(pokud unwrap proběhl)html—body_htmltext—body_text(z parse v1.4 nebo refetch v1.0)preview—body_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/. |