iniziale
This commit is contained in:
184
ArchiviazioneMail.py
Normal file
184
ArchiviazioneMail.py
Normal file
@@ -0,0 +1,184 @@
|
||||
import win32com.client
|
||||
import os
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta
|
||||
from tqdm import tqdm
|
||||
import time
|
||||
import locale
|
||||
|
||||
# Imposta la localizzazione in italiano
|
||||
try:
|
||||
locale.setlocale(locale.LC_TIME, "it_IT.UTF-8") # Per Windows/Linux moderni
|
||||
except: # noqa: E722
|
||||
try:
|
||||
locale.setlocale(locale.LC_TIME, "ita_ita") # Specifica per Windows
|
||||
except: # noqa: E722
|
||||
print("Impossibile impostare il locale italiano, uso i nomi manuali.")
|
||||
|
||||
# --- CONFIGURAZIONE ---
|
||||
ARCHIVE_NAME = "Archivio online - alessandro.battilani@intesasanpaolo.com"
|
||||
ONEDRIVE_PATH = r"C:\Users\U086304\OneDrive - Intesa SanPaolo\Allegati_Outlook"
|
||||
if not os.path.exists(ONEDRIVE_PATH):
|
||||
os.makedirs(ONEDRIVE_PATH)
|
||||
print(f"Cartella creata: {ONEDRIVE_PATH}")
|
||||
MONTHS_LIMIT = 5
|
||||
# ----------------------
|
||||
|
||||
def get_file_hash(file_path):
|
||||
hasher = hashlib.md5()
|
||||
with open(file_path, 'rb') as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
hasher.update(chunk)
|
||||
return hasher.hexdigest()
|
||||
|
||||
def main():
|
||||
if not os.path.exists(ONEDRIVE_PATH):
|
||||
os.makedirs(ONEDRIVE_PATH)
|
||||
|
||||
print("Connessione a Outlook in corso...")
|
||||
try:
|
||||
outlook = win32com.client.Dispatch("Outlook.Application")
|
||||
namespace = outlook.GetNamespace("MAPI")
|
||||
inbox = namespace.GetDefaultFolder(6)
|
||||
archive_root = namespace.Folders.Item(ARCHIVE_NAME)
|
||||
except Exception as e:
|
||||
print(f"Errore di connessione: {e}")
|
||||
return
|
||||
|
||||
cutoff_date = datetime.now() - timedelta(days=MONTHS_LIMIT * 30)
|
||||
filter_date_str = cutoff_date.strftime("%d/%m/%Y %H:%M")
|
||||
filter_str = f"[ReceivedTime] < '{filter_date_str}'"
|
||||
|
||||
items = inbox.Items.Restrict(filter_str)
|
||||
items.Sort("[ReceivedTime]", True)
|
||||
|
||||
total_items = items.Count
|
||||
if total_items == 0:
|
||||
print("Nessuna email da archiviare.")
|
||||
return
|
||||
|
||||
print(f"Trovate {total_items} email vecchie. Inizio archiviazione...")
|
||||
|
||||
processed_files = {}
|
||||
archived_count = 0
|
||||
mesi_it = {1: "Gennaio", 2: "Febbraio", 3: "Marzo", 4: "Aprile", 5: "Maggio", 6: "Giugno",
|
||||
7: "Luglio", 8: "Agosto", 9: "Settembre", 10: "Ottobre", 11: "Novembre", 12: "Dicembre"}
|
||||
|
||||
with tqdm(total=total_items, desc="Archiviazione", unit="mail", colour='green') as pbar:
|
||||
for i in range(total_items, 0, -1):
|
||||
try:
|
||||
item = items.Item(i)
|
||||
pbar.update(1)
|
||||
|
||||
if not hasattr(item, 'ReceivedTime'):
|
||||
continue
|
||||
|
||||
rt = item.ReceivedTime
|
||||
received_time = datetime(rt.year, rt.month, rt.day, rt.hour, rt.minute)
|
||||
anno_str = str(received_time.year)
|
||||
nome_mese = mesi_it[received_time.month]
|
||||
pbar.set_description(f"Mese: {nome_mese} {anno_str}")
|
||||
|
||||
# Cartelle
|
||||
month_folder_name = f"{received_time.month:02d}-{nome_mese}"
|
||||
try:
|
||||
y_f = archive_root.Folders.Item(anno_str)
|
||||
except: # noqa: E722
|
||||
y_f = archive_root.Folders.Add(anno_str)
|
||||
try:
|
||||
target_folder = y_f.Folders.Item(month_folder_name)
|
||||
except: # noqa: E722
|
||||
target_folder = y_f.Folders.Add(month_folder_name)
|
||||
|
||||
# --- TENTA LO SPOSTAMENTO CON RETRY ---
|
||||
archived_item = None
|
||||
for tentativo in range(3):
|
||||
try:
|
||||
archived_item = item.Move(target_folder)
|
||||
if archived_item:
|
||||
archived_item.Save()
|
||||
time.sleep(0.5) # Pausa vitale per il server
|
||||
break
|
||||
except: # noqa: E722
|
||||
time.sleep(1) # Aspetta se il server è occupato
|
||||
|
||||
if not archived_item:
|
||||
pbar.set_postfix(error="Move fallito")
|
||||
continue
|
||||
|
||||
# --- GESTIONE ALLEGATI (SOLO FILE VERI) ---
|
||||
if archived_item.Class == 43 and archived_item.Attachments.Count > 0:
|
||||
has_changed = False
|
||||
attachments = [archived_item.Attachments.Item(j) for j in range(1, archived_item.Attachments.Count + 1)]
|
||||
|
||||
for att in attachments:
|
||||
try:
|
||||
# 1. Filtro base: solo allegati di tipo "Valore" (file)
|
||||
if att.Type != 1:
|
||||
continue
|
||||
|
||||
# 2. FILTRO AVANZATO: Salta le immagini nelle firme (Inline Images)
|
||||
# Controlliamo se l'allegato ha un "Content-ID" (tipico delle immagini incorporate)
|
||||
try:
|
||||
prop_accessor = att.PropertyAccessor
|
||||
cid = prop_accessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E")
|
||||
if cid: # Se ha un CID, è un'immagine nel testo/firma
|
||||
continue
|
||||
except:
|
||||
# Se non riesce a leggere la proprietà, procediamo con cautela
|
||||
# (spesso i file veri non hanno questa proprietà)
|
||||
pass
|
||||
|
||||
# 3. FILTRO ESTENSIONI: Opzionale, se vuoi escludere png/jpg a prescindere
|
||||
# ext = os.path.splitext(att.FileName)[1].lower()
|
||||
# if ext in ['.png', '.jpg', '.jpeg', '.gif']: continue
|
||||
|
||||
# --- Procedura di salvataggio con DATA nel nome ---
|
||||
temp_path = os.path.join(os.environ['TEMP'], att.FileName)
|
||||
att.SaveAsFile(temp_path)
|
||||
f_hash = get_file_hash(temp_path)
|
||||
|
||||
# Creiamo il prefisso con la data (es. 2025-02-09_)
|
||||
date_prefix = received_time.strftime("%Y-%m-%d")
|
||||
|
||||
if f_hash not in processed_files:
|
||||
# Il nome file sarà: DATA_HASH_NOMEORIGINALE.ext
|
||||
# Usiamo l'hash corto (primi 6 caratteri) per non avere nomi infiniti
|
||||
unique_name = f"{date_prefix}_{f_hash[:6]}_{att.FileName}"
|
||||
dest_path = os.path.join(ONEDRIVE_PATH, unique_name)
|
||||
|
||||
if not os.path.exists(dest_path):
|
||||
os.replace(temp_path, dest_path)
|
||||
processed_files[f_hash] = dest_path
|
||||
else:
|
||||
dest_path = processed_files[f_hash]
|
||||
os.remove(temp_path)
|
||||
|
||||
# Il link nella mail punterà al nuovo nome con la data
|
||||
link_html = (
|
||||
f"<div style='border:1px solid #ccc; padding:8px; margin:10px 0; "
|
||||
f"background-color:#f3f3f3; font-family:Arial; font-size:12px;'>"
|
||||
f"<b>📎 Allegato spostato su OneDrive ({date_prefix}):</b><br>"
|
||||
f"<a href='file:///{dest_path}'>{att.FileName}</a></div>"
|
||||
)
|
||||
archived_item.HTMLBody = link_html + archived_item.HTMLBody
|
||||
att.Delete()
|
||||
has_changed = True
|
||||
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
if has_changed:
|
||||
archived_item.Save()
|
||||
|
||||
archived_count += 1
|
||||
pbar.set_postfix(archiviate=archived_count)
|
||||
|
||||
except Exception as e:
|
||||
pbar.set_postfix(last_err=str(e)[:15])
|
||||
continue
|
||||
|
||||
print(f"\nCompletato. Archiviate: {archived_count}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user