#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import re
import time
import json
import imaplib
import email
import traceback
import html as html_lib
from email.utils import parseaddr
from email.mime.text import MIMEText
from email.header import decode_header, make_header
from datetime import datetime
from pathlib import Path
import sys
# ==========================
# CONFIG da config.json (accanto allo script/EXE)
# ==========================
def app_dir() -> Path:
# se "frozen" (PyInstaller), usa la cartella dell'eseguibile
return Path(sys.executable).parent if getattr(sys, "frozen", False) else Path(__file__).resolve().parent
CONFIG_PATH = app_dir() / "config.json"
def load_config():
try:
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
cfg = json.load(f)
print(f"[CONFIG] Caricato: {CONFIG_PATH}")
return cfg
except Exception as e:
print(f"[ERRORE] Impossibile leggere {CONFIG_PATH}: {e}")
return {}
CFG = load_config()
# valori letti dal config (con default sensati)
IMAP_SERVER = CFG.get("imap_server", "mail.yogasoul.it")
EMAIL_ADDRESS = CFG.get("email_address", "")
EMAIL_PASSWORD = CFG.get("email_password", "")
OPENAI_API_KEY = CFG.get("openai_api_key", "")
OPENAI_MODEL = CFG.get("openai_model", "gpt-3.5-turbo")
PREFERRED_DRAFT_FOLDER = CFG.get("preferred_draft_folder", "BozzaRisposte")
MARK_AS_SEEN = bool(CFG.get("mark_as_seen", True))
THROTTLE_SECONDS = float(CFG.get("throttle_seconds", 0) or 0)
MAX_TO_PROCESS = int(CFG.get("max_to_process", 5) or 5)
# Percorsi esterni (accanto all'eseguibile)
KB_PATH = str(app_dir() / CFG.get("kb_path", "yogasoul_knowledge_base.json"))
PROMPT_PATH = str(app_dir() / CFG.get("prompt_path", "prompt_template.txt"))
# ==========================
# UTILS
# ==========================
def _decode_mime_words(s):
if not s:
return ""
try:
return str(make_header(decode_header(s)))
except Exception:
parts = decode_header(s)
out = []
for text, enc in parts:
if isinstance(text, bytes):
out.append(text.decode(enc or "utf-8", errors="ignore"))
else:
out.append(text or "")
return "".join(out)
def _html_to_text(html):
try:
text = re.sub(r"(?is)<(script|style).*?>.*?\1>", "", html or "")
text = re.sub(r"(?s)
", "\n", text)
text = re.sub(r"(?s)
{}
".format(html_lib.escape(p).replace("\n", "{}
".format(html_lib.escape(s).replace("\n", "— Messaggio originale —
" f"{escaped}" ) def _inject_before_body_end(html_src: str, addition: str) -> str: """Inserisce 'addition' prima di in modo case-insensitive; se manca