189 lines
6.4 KiB
Python
189 lines
6.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# =============================================================================
|
|
# Nazev: jnj_dump_recipient_msgs_v1.0.py
|
|
# Verze: 1.0
|
|
# Datum: 2026-06-16
|
|
# Popis: JNJ-native (MAPI / pywin32). Najde VSECHNY e-maily danemu prijemci
|
|
# (default Hustak) napric vybranymi slozkami, ULOZI je jako .msg a
|
|
# u kazdeho VYPISE diagnosticke MAPI vlastnosti precteni ze ZIVE
|
|
# polozky (Message-ID 0x1035, SenderName 0x0C1A, SentRepresentingName
|
|
# 0x0042, addrtype 0x0C1E/0x0064, ReportText 0x1001, PrimarySendAccount
|
|
# 0x0E28, MessageFlags 0x0E07, item.Sent). Cil: porovnat, zda tyto
|
|
# vlastnosti zustanou i v ulozenem .msg (olefile kontrola doma).
|
|
# Pouziti: Spustit v JNJ Pythonu (Thonny), Outlook s JNJ schrankou.
|
|
# pip install pywin32 ; python jnj_dump_recipient_msgs_v1.0.py
|
|
# Skript NIC neodesila ani nemaze, jen CTE a uklada .msg kopie.
|
|
# =============================================================================
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import win32com.client # pywin32
|
|
|
|
# ----------------------------- KONFIGURACE -----------------------------------
|
|
|
|
SENDER_SMTP = "vbuzalka@its.jnj.com" # ucet (jeho store se prohledava)
|
|
TARGET_EMAIL = "rastislav.hustak@fntt.sk" # koho hledame (To NEBO Cc)
|
|
|
|
# Slozky k prohledani (shoda na NAZEV slozky kdekoli ve strome; vc. podslozek).
|
|
# Prazdny seznam + SCAN_ALL=True => projde celou schranku (pomale!).
|
|
SCAN_FOLDERS = ["Sent Items", "Drafts", "Deleted Items", "Archive", "Inbox"]
|
|
SCAN_ALL = False
|
|
|
|
# Kam ulozit .msg kopie (na JNJ stroji). Vytvori se, kdyz neexistuje.
|
|
OUTPUT_DIR = r"C:\Users\vbuzalka\hustak_dump"
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
OL_MSG = 3 # olMSG (SaveAs typ)
|
|
OL_FOLDER_SENT = 5
|
|
PA = "http://schemas.microsoft.com/mapi/proptag/0x{:s}"
|
|
|
|
# Diagnosticke tagy (PT_UNICODE 001F, dlouhe 0003)
|
|
TAGS = [
|
|
("Msg-ID", "1035001F"),
|
|
("SenderName", "0C1A001F"),
|
|
("SenderAddrType", "0C1E001F"),
|
|
("SentRepName", "0042001F"),
|
|
("SentRepAddrType", "0064001F"),
|
|
("ReportText", "1001001F"),
|
|
("PrimarySendAcct", "0E28001F"),
|
|
]
|
|
TAG_MSGFLAGS = "0E070003"
|
|
TAG_RCPT_ADDRTYPE = "3002001F"
|
|
|
|
|
|
def smtp_of(recipient):
|
|
try:
|
|
return (recipient.PropertyAccessor.GetProperty(PA.format("39FE001E")) or "").lower()
|
|
except Exception:
|
|
try:
|
|
return (recipient.Address or "").lower()
|
|
except Exception:
|
|
return ""
|
|
|
|
|
|
def get_prop(item, tag):
|
|
try:
|
|
v = item.PropertyAccessor.GetProperty(PA.format(tag))
|
|
return v
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def get_store_root(ns):
|
|
try:
|
|
for acct in ns.Accounts:
|
|
if (acct.SmtpAddress or "").lower() == SENDER_SMTP.lower():
|
|
return acct.DeliveryStore.GetRootFolder()
|
|
except Exception:
|
|
pass
|
|
return ns.GetDefaultFolder(OL_FOLDER_SENT).Parent # fallback: koren default store
|
|
|
|
|
|
def iter_target_folders(root):
|
|
"""Yield slozek, ktere se maji skenovat (dle nazvu + jejich podslozky)."""
|
|
def walk(folder, inscope):
|
|
scope = inscope or SCAN_ALL or (folder.Name in SCAN_FOLDERS)
|
|
if scope:
|
|
yield folder
|
|
try:
|
|
for sub in folder.Folders:
|
|
yield from walk(sub, scope)
|
|
except Exception:
|
|
pass
|
|
yield from walk(root, False)
|
|
|
|
|
|
def safe(s, n=40):
|
|
s = re.sub(r"[^A-Za-z0-9._-]+", "_", (s or ""))
|
|
return s[:n].strip("_")
|
|
|
|
|
|
def matches_target(item):
|
|
"""Vrati ('To'/'Cc') kdyz je TARGET_EMAIL mezi prijemci, jinak None."""
|
|
tgt = TARGET_EMAIL.lower()
|
|
try:
|
|
for r in item.Recipients:
|
|
if smtp_of(r) == tgt:
|
|
return {1: "To", 2: "Cc", 3: "Bcc"}.get(r.Type, "To")
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
def main():
|
|
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
|
outlook = win32com.client.Dispatch("Outlook.Application")
|
|
ns = outlook.GetNamespace("MAPI")
|
|
root = get_store_root(ns)
|
|
|
|
print(f"Hledam e-maily, kde je prijemce: {TARGET_EMAIL}")
|
|
print(f"Slozky: {'VSE' if SCAN_ALL else ', '.join(SCAN_FOLDERS)}")
|
|
print(f"Vystup .msg: {OUTPUT_DIR}")
|
|
print("=" * 90)
|
|
|
|
idx = 0
|
|
for folder in iter_target_folders(root):
|
|
try:
|
|
items = folder.Items
|
|
except Exception:
|
|
continue
|
|
for it in list(items):
|
|
try:
|
|
if it.Class != 43: # olMail
|
|
continue
|
|
except Exception:
|
|
continue
|
|
role = matches_target(it)
|
|
if not role:
|
|
continue
|
|
idx += 1
|
|
|
|
# --- diagnostika ze ZIVE polozky ---
|
|
try:
|
|
sent_flag = it.Sent
|
|
except Exception:
|
|
sent_flag = "?"
|
|
flags = get_prop(it, TAG_MSGFLAGS)
|
|
props = {label: get_prop(it, tag) for label, tag in TAGS}
|
|
try:
|
|
sent_on = it.SentOn
|
|
except Exception:
|
|
sent_on = None
|
|
try:
|
|
entry_tail = (it.EntryID or "")[-20:]
|
|
except Exception:
|
|
entry_tail = ""
|
|
|
|
print(f"\n[{idx}] slozka='{folder.Name}' role={role} Sent={sent_flag} flags={flags}")
|
|
print(f" subject : {getattr(it,'Subject','')}")
|
|
print(f" sent_on : {sent_on}")
|
|
print(f" Msg-ID : {props['Msg-ID']}")
|
|
print(f" SenderName : {props['SenderName']} (addrtype {props['SenderAddrType']})")
|
|
print(f" SentRepName : {props['SentRepName']} (addrtype {props['SentRepAddrType']})")
|
|
print(f" PrimarySendAcct: {props['PrimarySendAcct']}")
|
|
rt = props["ReportText"]
|
|
print(f" ReportText 0x1001: {'ANO -> ' + repr(rt[:120]) if rt else '-'}")
|
|
|
|
# --- ulozeni .msg ---
|
|
fn = f"{idx:02d}_{safe(folder.Name,18)}_{safe(getattr(it,'Subject',''),28)}_{entry_tail}.msg"
|
|
path = os.path.join(OUTPUT_DIR, fn)
|
|
try:
|
|
it.SaveAs(path, OL_MSG)
|
|
print(f" ulozeno: {fn}")
|
|
except Exception as e:
|
|
print(f" !! SaveAs chyba: {e}")
|
|
|
|
print("\n" + "=" * 90)
|
|
print(f"Hotovo. Nalezeno a ulozeno: {idx} polozek do {OUTPUT_DIR}")
|
|
print("Prines .msg domu a porovnej vlastnosti olefilem (zive vs ulozene).")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except Exception as e:
|
|
print("CHYBA:", e)
|
|
sys.exit(1)
|