Add NAS daily email report wrapper
Prompts used since the previous commit: - Hoe regelen we het dagrapport? - Het rapport moet per email naar mij gestuurd worden. - hans@australius.nl - akkoord Special observations: - dagelijks_overzicht.py now supports --mail-to and sends zero-message reports. - QNAP plain deployment installs run_daily_report.sh with DAILY_REPORT_TO defaulting to hans@australius.nl. - verplaats_log.json remains modified from the user's run and was intentionally not staged.
This commit is contained in:
+20
-10
@@ -7,7 +7,7 @@ berichten die gisteren zijn binnengekomen, en stuurt een HTML-samenvatting
|
||||
naar hans@australius.nl.
|
||||
|
||||
Eenmalige setup: python3 dagelijks_overzicht.py --setup
|
||||
Handmatig testen: python3 dagelijks_overzicht.py [--gisteren | --datum 2026-07-01]
|
||||
Handmatig testen: python3 dagelijks_overzicht.py [--gisteren | --datum 2026-07-01] [--mail-to adres]
|
||||
Plannen (macOS): zie dagelijks_overzicht.plist
|
||||
"""
|
||||
|
||||
@@ -27,6 +27,7 @@ from collections import defaultdict
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from imap_utils import list_folders, quote_mailbox
|
||||
from mail_imap_ops import fetch_body
|
||||
|
||||
# Mappen die NIET in het overzicht komen (systeem + inbox zelf)
|
||||
UITSLUITINGEN = {
|
||||
@@ -59,6 +60,17 @@ def load_config():
|
||||
}
|
||||
|
||||
|
||||
def arg_value(args: list[str], flag: str) -> str | None:
|
||||
"""Return the value after a simple command-line flag."""
|
||||
if flag not in args:
|
||||
return None
|
||||
idx = args.index(flag)
|
||||
if idx + 1 >= len(args):
|
||||
print(f"Ontbrekende waarde voor {flag}")
|
||||
sys.exit(2)
|
||||
return args[idx + 1]
|
||||
|
||||
|
||||
# ── helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
def decode_hdr(value) -> str:
|
||||
@@ -128,10 +140,11 @@ def haal_berichten_op(mail: imaplib.IMAP4_SSL, zoekdatum: date) -> dict[str, lis
|
||||
status, msg_data = mail.fetch(
|
||||
uid, "(BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE)])"
|
||||
)
|
||||
if status != "OK" or not msg_data or not msg_data[0]:
|
||||
raw_header = fetch_body(msg_data)
|
||||
if status != "OK" or raw_header is None:
|
||||
continue
|
||||
|
||||
msg = email.message_from_bytes(msg_data[0][1])
|
||||
msg = email.message_from_bytes(raw_header)
|
||||
from_raw = decode_hdr(msg.get("From", ""))
|
||||
subject = decode_hdr(msg.get("Subject", "(geen onderwerp)"))
|
||||
date_str = msg.get("Date", "")
|
||||
@@ -232,12 +245,13 @@ def stuur_overzicht(html: str, zoekdatum: date, config: dict):
|
||||
def run():
|
||||
args = sys.argv[1:]
|
||||
cfg_dict = load_config()
|
||||
if mail_to := arg_value(args, "--mail-to"):
|
||||
cfg_dict["to"] = mail_to
|
||||
print(f"Account: {cfg_dict['username']}\n")
|
||||
|
||||
# Datum bepalen
|
||||
if "--datum" in args:
|
||||
idx = args.index("--datum")
|
||||
zoekdatum = date.fromisoformat(args[idx + 1])
|
||||
if datum := arg_value(args, "--datum"):
|
||||
zoekdatum = date.fromisoformat(datum)
|
||||
else:
|
||||
zoekdatum = date.today() - timedelta(days=1) # standaard: gisteren
|
||||
|
||||
@@ -256,10 +270,6 @@ def run():
|
||||
totaal = sum(len(v) for v in berichten.values())
|
||||
print(f"{totaal} berichten gevonden in {len(berichten)} mappen.")
|
||||
|
||||
if totaal == 0:
|
||||
print("Niets te sturen.")
|
||||
return
|
||||
|
||||
html = bouw_html(berichten, zoekdatum)
|
||||
stuur_overzicht(html, zoekdatum, cfg_dict)
|
||||
|
||||
|
||||
+21
-3
@@ -14,6 +14,7 @@ fi
|
||||
APP_USER="${APP_USER:-mailcat}"
|
||||
SERVICE_NAME="${SERVICE_NAME:-mailcat-sort-backup}"
|
||||
ACCOUNT="${ACCOUNT:-backup}"
|
||||
DAILY_REPORT_TO="${DAILY_REPORT_TO:-hans@australius.nl}"
|
||||
REMOTE_PATH="${REMOTE_PATH:-/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}"
|
||||
SERVICE_MANAGER="${SERVICE_MANAGER:-systemd}"
|
||||
MAILCAT_HOME="${MAILCAT_HOME:-/share/homes/$APP_USER}"
|
||||
@@ -202,11 +203,27 @@ echo "logs: \$LOG_DIR"
|
||||
exit 1
|
||||
STATUS_SCRIPT
|
||||
|
||||
cat > /tmp/mailcat-daily-report.sh <<REPORT_SCRIPT
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
APP_DIR="$APP_DIR"
|
||||
ACCOUNT="$ACCOUNT"
|
||||
PYTHON="$PYTHON"
|
||||
LOG_DIR="$LOG_DIR"
|
||||
MAIL_TO="$DAILY_REPORT_TO"
|
||||
MKDIR="$MKDIR"
|
||||
|
||||
"\$MKDIR" -p "\$LOG_DIR"
|
||||
cd "\$APP_DIR"
|
||||
"\$PYTHON" "\$APP_DIR/dagelijks_overzicht.py" --account "\$ACCOUNT" --mail-to "\$MAIL_TO" >> "\$LOG_DIR/daily_report.out" 2>&1
|
||||
REPORT_SCRIPT
|
||||
|
||||
"$SUDO" "$INSTALL" -m 0755 /tmp/mailcat-start.sh "$APP_DIR/bin/start_mailcat.sh"
|
||||
"$SUDO" "$INSTALL" -m 0755 /tmp/mailcat-stop.sh "$APP_DIR/bin/stop_mailcat.sh"
|
||||
"$SUDO" "$INSTALL" -m 0755 /tmp/mailcat-status.sh "$APP_DIR/bin/status_mailcat.sh"
|
||||
"$SUDO" "$CHOWN" "$APP_USER:$APP_GROUP" "$APP_DIR/bin/start_mailcat.sh" "$APP_DIR/bin/stop_mailcat.sh" "$APP_DIR/bin/status_mailcat.sh"
|
||||
"$RM" -f /tmp/mailcat-start.sh /tmp/mailcat-stop.sh /tmp/mailcat-status.sh
|
||||
"$SUDO" "$INSTALL" -m 0755 /tmp/mailcat-daily-report.sh "$APP_DIR/bin/run_daily_report.sh"
|
||||
"$SUDO" "$CHOWN" "$APP_USER:$APP_GROUP" "$APP_DIR/bin/start_mailcat.sh" "$APP_DIR/bin/stop_mailcat.sh" "$APP_DIR/bin/status_mailcat.sh" "$APP_DIR/bin/run_daily_report.sh"
|
||||
"$RM" -f /tmp/mailcat-start.sh /tmp/mailcat-stop.sh /tmp/mailcat-status.sh /tmp/mailcat-daily-report.sh
|
||||
else
|
||||
"$SUDO" "$INSTALL" -m 0644 "$APP_DIR/systemd/mailcat-sort.service" "/etc/systemd/system/$SERVICE_NAME.service"
|
||||
"$SUDO" "$SED" -i \
|
||||
@@ -229,6 +246,7 @@ echo "Create/check $APP_DIR/config.json, then run:"
|
||||
if [ "$SERVICE_MANAGER" = "plain" ]; then
|
||||
echo " sudo -u $APP_USER $APP_DIR/bin/start_mailcat.sh"
|
||||
echo " sudo -u $APP_USER $APP_DIR/bin/status_mailcat.sh"
|
||||
echo " sudo -u $APP_USER $APP_DIR/bin/run_daily_report.sh"
|
||||
echo " tail -f $LOG_DIR/sort_mail_daemon.log"
|
||||
else
|
||||
echo " sudo systemctl start $SERVICE_NAME.service"
|
||||
@@ -239,5 +257,5 @@ REMOTE
|
||||
scp "$ARCHIVE" "$REMOTE_USER@$REMOTE_HOST:/tmp/mailcat-deploy.tar.gz"
|
||||
scp "$REMOTE_SCRIPT" "$REMOTE_USER@$REMOTE_HOST:/tmp/mailcat-remote-deploy.sh"
|
||||
|
||||
REMOTE_COMMAND=$(printf "APP_USER=%q APP_DIR=%q SERVICE_NAME=%q ACCOUNT=%q REMOTE_PATH=%q SERVICE_MANAGER=%q MAILCAT_HOME=%q STATE_DIR=%q LOG_DIR=%q RUN_DIR=%q PID_FILE=%q PYTHON=%q SUDO=%q USERADD=%q INSTALL=%q SED=%q TAR=%q MKDIR=%q CHOWN=%q CHMOD=%q RM=%q SYSTEMCTL=%q /bin/sh /tmp/mailcat-remote-deploy.sh" "$APP_USER" "$APP_DIR" "$SERVICE_NAME" "$ACCOUNT" "$REMOTE_PATH" "$SERVICE_MANAGER" "$MAILCAT_HOME" "$STATE_DIR" "$LOG_DIR" "$RUN_DIR" "$PID_FILE" "$PYTHON" "$SUDO" "$USERADD" "$INSTALL" "$SED" "$TAR" "$MKDIR" "$CHOWN" "$CHMOD" "$RM" "$SYSTEMCTL")
|
||||
REMOTE_COMMAND=$(printf "APP_USER=%q APP_DIR=%q SERVICE_NAME=%q ACCOUNT=%q DAILY_REPORT_TO=%q REMOTE_PATH=%q SERVICE_MANAGER=%q MAILCAT_HOME=%q STATE_DIR=%q LOG_DIR=%q RUN_DIR=%q PID_FILE=%q PYTHON=%q SUDO=%q USERADD=%q INSTALL=%q SED=%q TAR=%q MKDIR=%q CHOWN=%q CHMOD=%q RM=%q SYSTEMCTL=%q /bin/sh /tmp/mailcat-remote-deploy.sh" "$APP_USER" "$APP_DIR" "$SERVICE_NAME" "$ACCOUNT" "$DAILY_REPORT_TO" "$REMOTE_PATH" "$SERVICE_MANAGER" "$MAILCAT_HOME" "$STATE_DIR" "$LOG_DIR" "$RUN_DIR" "$PID_FILE" "$PYTHON" "$SUDO" "$USERADD" "$INSTALL" "$SED" "$TAR" "$MKDIR" "$CHOWN" "$CHMOD" "$RM" "$SYSTEMCTL")
|
||||
ssh -tt "$REMOTE_USER@$REMOTE_HOST" "$REMOTE_COMMAND"
|
||||
|
||||
+14
-3
@@ -91,6 +91,14 @@ Scripts using shared IMAP folder handling:
|
||||
- `dagelijks_overzicht.py`
|
||||
- `sort_mail_daemon.py`
|
||||
|
||||
`dagelijks_overzicht.py` current behavior:
|
||||
|
||||
- Builds an HTML digest of messages that arrived on the selected date, defaulting to yesterday.
|
||||
- Uses central `config.py`; select the mailbox account with `--account`, for example `--account backup`.
|
||||
- Sends the digest via SMTP using the selected account credentials.
|
||||
- Supports `--mail-to <address>`; the NAS daily report target is `hans@australius.nl`.
|
||||
- Sends the report even when zero messages are found, so scheduled runs are visible.
|
||||
|
||||
Shared operation helpers:
|
||||
|
||||
- `mail_imap_ops.py` centralizes header decoding, FETCH payload extraction, routing fetched headers to full IMAP destinations, destination mailbox creation, and copy/delete move semantics for live sorting.
|
||||
@@ -153,7 +161,8 @@ Remote/NAS deployment files:
|
||||
- For the local NAS, use `SERVICE_MANAGER=plain`, `MAILCAT_HOME=/share/homes/mailcat`, and `PYTHON=/opt/bin/python3`. In plain mode, default paths are `APP_DIR=$MAILCAT_HOME/mailcat`, `STATE_DIR=$APP_DIR/state`, `LOG_DIR=$APP_DIR/log`, `RUN_DIR=$APP_DIR/run`, and `PID_FILE=$RUN_DIR/mailcat.pid`.
|
||||
- QNAP path facts supplied by the user: `SUDO=/usr/bin/sudo`, `USERADD=/usr/local/bin/useradd`, `PYTHON=/opt/bin/python3`, `INSTALL=/usr/bin/install`, `SED=/bin/sed`, `TAR=/bin/tar`, `MKDIR=/bin/mkdir`, `CHOWN=/bin/chown`, `CHMOD=/bin/chmod`, and `RM=/bin/rm`. `nologin`, `python3`, `systemctl`, and `nohup` are not in the user's default PATH.
|
||||
- The deploy script accepts absolute tool overrides for those commands and passes them into the remote script. In plain mode, the generated start/stop/status scripts also bake in absolute `MKDIR` and `RM` paths.
|
||||
- In plain mode, the deploy script does not install systemd files. It writes executable scripts to `$APP_DIR/bin/start_mailcat.sh`, `$APP_DIR/bin/stop_mailcat.sh`, and `$APP_DIR/bin/status_mailcat.sh`.
|
||||
- In plain mode, the deploy script does not install systemd files. It writes executable scripts to `$APP_DIR/bin/start_mailcat.sh`, `$APP_DIR/bin/stop_mailcat.sh`, `$APP_DIR/bin/status_mailcat.sh`, and `$APP_DIR/bin/run_daily_report.sh`.
|
||||
- `DAILY_REPORT_TO` controls the report recipient during deploy and defaults to `hans@australius.nl`. The daily report wrapper appends output to `$LOG_DIR/daily_report.out`.
|
||||
- `systemd/mailcat-sort.service` is a template consumed by `deploy_vps.sh`; after placeholder replacement it runs `sort_mail_daemon.py --account backup --folder INBOX` and stores state/logs under `/var/lib/mailcat` and `/var/log/mailcat`.
|
||||
- `config.json` remains ignored and must be created manually on the remote host with mode `600` before starting the service.
|
||||
|
||||
@@ -255,8 +264,10 @@ Recommended next work:
|
||||
- `sudo -u mailcat /share/homes/mailcat/mailcat/bin/start_mailcat.sh`
|
||||
- `sudo -u mailcat /share/homes/mailcat/mailcat/bin/status_mailcat.sh`
|
||||
- `tail -f /share/homes/mailcat/mailcat/log/sort_mail_daemon.log`
|
||||
4. Send controlled test messages to `backup@australius.nl` for invoices, clear AI providers, a newsletter/service route, and unmatched mail; verify expected folder moves and daemon logs.
|
||||
5. Add `List-ID` header support to mailing-list routing if live daemon tests show sender/domain matching is too coarse.
|
||||
4. Test the daily report manually: `sudo -u mailcat /share/homes/mailcat/mailcat/bin/run_daily_report.sh`, then check `tail -100 /share/homes/mailcat/mailcat/log/daily_report.out` and confirm mail delivery to `hans@australius.nl`.
|
||||
5. Add the daily report wrapper to QNAP scheduler after the manual test succeeds.
|
||||
6. Send controlled test messages to `backup@australius.nl` for invoices, clear AI providers, a newsletter/service route, and unmatched mail; verify expected folder moves and daemon logs.
|
||||
7. Add `List-ID` header support to mailing-list routing if live daemon tests show sender/domain matching is too coarse.
|
||||
|
||||
## Persistent File Rule
|
||||
|
||||
|
||||
Reference in New Issue
Block a user