Files
janssen/Feasibility/jnj_scan_failed_sent_v1.0.py
T
2026-06-17 15:05:10 +02:00

192 lines
6.0 KiB
Python

# -*- coding: utf-8 -*-
# =============================================================================
# Nazev: jnj_scan_failed_sent_v1.0.py
# Verze: 1.0
# Datum: 2026-06-16
# Popis: JNJ-native (MAPI / pywin32). Projde slozku Odeslane (Sent Items) za
# poslednich N dni a najde PODEZRELE e-maily = pravdepodobne NEODESLANE
# (napr. SendAs denied). Kazdy podezrely ULOZI jako .msg a vypise, ktere
# priznaky se trefily. NIC neodesila ani nemaze, jen CTE a uklada.
# Priznaky podezreni (cteno ze ZIVE polozky):
# FAIL_BODY = telo/ReportText obsahuje "could not be sent" / "SendAsDenied"
# / "permission to send the message on behalf" / "TransportSend"
# SENDAS_BUZ = PrimarySendAccount/SentRepresenting/Sender obsahuje "buzalka.cz"
# NO_MSGID = chybi Internet Message-ID (0x1035) -- slabsi priznak
# Pouziti: JNJ Python (Thonny), Outlook s JNJ schrankou.
# pip install pywin32 ; python jnj_scan_failed_sent_v1.0.py
# =============================================================================
import os
import re
import sys
import datetime
import win32com.client # pywin32
# ----------------------------- KONFIGURACE -----------------------------------
SENDER_SMTP = "vbuzalka@its.jnj.com"
DAYS = 60 # okno: poslednich N dni
OUTPUT_DIR = r"C:\Users\vbuzalka\sent_suspects"
# Ukladat i polozky, ktere maji JEN slaby priznak NO_MSGID (bez FAIL/SENDAS)?
# True = vc. provizornich kopii bez Message-ID (muze byt vic souboru).
INCLUDE_NO_MSGID = True
# -----------------------------------------------------------------------------
OL_MSG = 3
OL_FOLDER_SENT = 5
PA = "http://schemas.microsoft.com/mapi/proptag/0x{:s}"
P_MSGID = "1035001F"
P_SENDACCT = "0E28001F" # PrimarySendAccount
P_SENTREP_EM = "0065001F" # SentRepresentingEmailAddress
P_SENDER_EM = "0C1F001F" # SenderEmailAddress
P_REPORTTEXT = "1001001F" # ReportText (kdyz existuje)
FAIL_SIGNS = [
"could not be sent",
"sendasdenied",
"permission to send the message on behalf",
"transportsend operation has failed",
"mapiexceptionsendasdenied",
"tuto zpravu nelze odeslat", # pro pripad lokalizace
]
def gp(item, tag):
try:
return item.PropertyAccessor.GetProperty(PA.format(tag))
except Exception:
return None
def get_sent_folder(ns):
try:
for acct in ns.Accounts:
if (acct.SmtpAddress or "").lower() == SENDER_SMTP.lower():
return acct.DeliveryStore.GetDefaultFolder(OL_FOLDER_SENT)
except Exception:
pass
return ns.GetDefaultFolder(OL_FOLDER_SENT)
def safe(s, n=34):
return re.sub(r"[^A-Za-z0-9._-]+", "_", (s or ""))[:n].strip("_")
def analyze(item):
"""Vrati seznam priznaku (flags) pro polozku."""
flags = []
# 1) FAIL_BODY: telo + ReportText
blob = ""
try:
blob += (item.Body or "")
except Exception:
pass
rt = gp(item, P_REPORTTEXT)
if rt:
blob += "\n" + str(rt)
low = blob.lower()
if any(s in low for s in FAIL_SIGNS):
flags.append("FAIL_BODY")
# 2) SENDAS_BUZ: nektera z odesilatelskych poloz. obsahuje buzalka.cz
for tag in (P_SENDACCT, P_SENTREP_EM, P_SENDER_EM):
v = gp(item, tag)
if v and "buzalka.cz" in str(v).lower():
flags.append("SENDAS_BUZ")
break
# 3) NO_MSGID
mid = gp(item, P_MSGID)
if not mid:
flags.append("NO_MSGID")
return flags, (mid or "")
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
cutoff = datetime.date.today() - datetime.timedelta(days=DAYS)
outlook = win32com.client.Dispatch("Outlook.Application")
ns = outlook.GetNamespace("MAPI")
sent = get_sent_folder(ns)
items = sent.Items
items.Sort("[SentOn]", True) # nejnovejsi prvni
print(f"Slozka : {sent.FolderPath}")
print(f"Okno : poslednich {DAYS} dni (od {cutoff.isoformat()})")
print(f"Vystup : {OUTPUT_DIR}")
print(f"NO_MSGID se uklada: {INCLUDE_NO_MSGID}")
print("=" * 90)
scanned = saved = strong = 0
for it in list(items):
try:
if it.Class != 43:
continue
except Exception:
continue
# datum + early stop
try:
s = it.SentOn
sdate = datetime.date(s.year, s.month, s.day)
except Exception:
sdate = None
if sdate is not None:
if sdate < cutoff:
break # dale uz jen starsi (serazeno desc)
scanned += 1
flags, mid = analyze(it)
if not flags:
continue
is_strong = ("FAIL_BODY" in flags) or ("SENDAS_BUZ" in flags)
if not is_strong and not (INCLUDE_NO_MSGID and "NO_MSGID" in flags):
continue
saved += 1
if is_strong:
strong += 1
subj = ""
try:
subj = it.Subject or ""
except Exception:
pass
try:
tail = (it.EntryID or "")[-20:]
except Exception:
tail = ""
tagstr = "+".join(flags)
print(f"\n[{saved}] {sdate} flags={tagstr}")
print(f" subj : {subj}")
print(f" msgid: {mid if mid else '<chybi>'}")
fn = f"{('STRONG' if is_strong else 'weak')}_{sdate}_{safe(subj,30)}_{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"Prohledano (v okne): {scanned}")
print(f"Ulozeno podezrelych: {saved} (z toho silnych FAIL/SENDAS: {strong})")
print(f"Soubory v: {OUTPUT_DIR} -> prines je domu ke kontrole.")
print("Pozn.: STRONG_* = telo NDR nebo send-account buzalka.cz (skoro jiste neodeslano).")
print(" weak_* = jen chybi Message-ID (muze byt i provizorni kopie, co se pozdeji dokonci).")
if __name__ == "__main__":
try:
main()
except Exception as e:
print("CHYBA:", e)
sys.exit(1)