Metadata-Version: 2.5
Name: cde-mailer
Version: 0.1.0
Summary: Template-based email dispatch and mail-merge tool for CdE e.V. with Zammad integration
Project-URL: Homepage, https://tracker.cde-ev.de/orgas/cde-mailer
Project-URL: Repository, https://tracker.cde-ev.de/orgas/cde-mailer.git
Project-URL: Documentation, https://tracker.cde-ev.de/orgas/cde-mailer/src/branch/main/docs
Project-URL: Bug Tracker, https://tracker.cde-ev.de/orgas/cde-mailer/issues
Author-email: Tobias Wackenhut <tobias@wackenhut.at>
Maintainer: CdE e.V. Orgas
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: cde,cli,email,jinja2,mail-merge,zammad
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Email
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: jinja2>=3.0.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5.0; extra == 'dev'
Description-Content-Type: text/markdown

# cde-mailer

`cde-mailer` is a template-based email dispatch and mail-merge tool developed for **CdE e.V.** (Club der Ehemaligen der Deutschen SchülerAkademien e.V.). It renders personalized emails from `.cdemail` job files and semicolon-delimited CSV exports using **Jinja2**, generates local HTML previews for mandatory proof-reading, and dispatches messages reliably through **Zammad** with built-in ticket deduplication.

---

## Key Features

- **Jinja2 Mail-Merge**: Render subjects, recipients (`To`, `CC`, `BCC`), tags, attachment paths, metadata, and bodies from CdE CSV exports (`;` delimiter, automatic `persona.forename` $\rightarrow$ `persona_forename` variable aliasing).
- **Two-Stage Safety Workflow**: Automatically generates HTML previews in `email_previews/` and pauses on first run (or whenever templates/data change) so you can proof-read before any network action occurs.
- **Pre-Dispatch Verification Dry-Run**: Queries Zammad before sending to report the exact number of new tickets, appended articles, and ticket state changes that will occur.
- **Smart Zammad Deduplication**: Uses Elasticsearch queries on customer address, group, tags, and article subject to prevent duplicate sends (`skipifexists=true`) and thread follow-up emails into existing tickets.
- **Open-Ticket Protection**: Refuses to automatically close existing tickets that are currently open (e.g., when a participant has replied).
- **Audit Logging**: Records every dispatched message with timestamp, recipient, subject, status, and Zammad ticket/article ID in `sent_emails.log.csv`.

---

## Workflow Overview

```mermaid
flowchart LR
    A[".cdemail + .csv"] --> B["EmailJob Parser\n& Validator"]
    B --> C{"Previews\nChanged?"}
    C -- "Yes (1st Run)" --> D["Write HTML Previews\n(email_previews/) & Exit"]
    C -- "No (2nd Run)" --> E["Zammad Dry-Run\n& Statistics"]
    E --> F{"Confirm\n(y/N)?"}
    F -- "Yes" --> G["Dispatch via Zammad API\n& Log to sent_emails.log.csv"]
```

---

## Installation

### Using `uv` (Recommended)

```bash
git clone https://tracker.cde-ev.de/orgas/cde-mailer.git
cd cde-mailer
uv sync
```

### Using `pip`

```bash
pip install .
```

---

## Quickstart

### 1. Create a Configuration File (`config.toml`)

Copy [`config.toml.example`](config.toml.example) to `config.toml` and configure your Zammad group and API token:

```toml
backend = "zammad"
host = "tickets.cde-ev.de"
token_file = "zammad_token.txt"
zammad_group = "PfingstAkademie 2026"
```

*(Tip: `cde-mailer` also supports embedding these settings under a `[cde-mailer]` table inside a shared `config.toml`.)*

### 2. Author an Email Job (`welcome.cdemail`)

Create a `.cdemail` file with a header and body separated by `---`:

```text
Subject=Welcome to the academy, {{ persona_forename }}!
To={{ persona_username }}
CSV=participants.csv
Attachments= handbooks/handbook.pdf
Tags=pa2026, welcome
state=closed
skipifexists=true
---
Hello {{ persona_forename }},

We are delighted to welcome you to the academy!
Please find the participant handbook attached.

Best regards,
Your Orga Team
```

### 3. Generate Previews & Dispatch

1. **First run — generate HTML previews**:
   ```bash
   uv run cde-mailer welcome.cdemail
   ```
   Inspect the generated files in `email_previews/welcome/email_*.html`.
2. **Second run — dry-run check & interactive dispatch**:
   ```bash
   uv run cde-mailer welcome.cdemail
   ```
   Review the dry-run summary of new tickets/articles and confirm with `y` to send.

---

## CLI Options Summary

```text
cde-mailer [options] <job_files>...
```

| Option | Description |
| :--- | :--- |
| `job_files` | One or more paths to `.cdemail` job files. |
| `--config <path>` | Path to TOML configuration file (default: `config.toml`). |
| `--host <host>` | Zammad host (overrides config). |
| `--group <group>` | Zammad group for tickets (overrides config). |
| `--token <token>` | Zammad API token string (overrides config). |
| `--token_file <path>` | Path to Zammad API token file (overrides config). |
| `--backend {zammad}` | Dispatch backend to use (default: `zammad`). |
| `--dry-run` | Simulation mode: run Zammad verification checks without sending emails. |
| `--non-interactive` | Skip preview-change pause and confirmation prompts. |
| `-v`, `--verbose` | Increase logging verbosity (`-v` for `INFO`, `-vv` for `DEBUG`). |

---

## Python API Usage

`cde-mailer` can also be used as a typed Python library:

```python
import pathlib
from cde_mailer import EmailJob, get_sender, process_email_job_files

# High-level orchestration
config = {
    "backend": "zammad",
    "host": "tickets.cde-ev.de",
    "token_file": "zammad_token.txt",
    "zammad_group": "PfingstAkademie 2026",
}
process_email_job_files([pathlib.Path("welcome.cdemail")], config)

# Or granular message inspection
job = EmailJob.load(pathlib.Path("welcome.cdemail"))
for message in job:
    print(message.to, message.subject)
```

---

## Documentation & Contributing

Full documentation is available in the [**`docs/`**](docs/README.md) directory:

- [**Documentation Overview**](docs/README.md)
- [**CLI Usage & Dispatch Workflow**](docs/usage.md)
- [**Configuration & Authentication**](docs/configuration.md)
- [**Email Job Files (`.cdemail`) & Templating**](docs/job-files.md)
- [**Zammad Backend & Deduplication Internals**](docs/zammad-backend.md)
- [**Architecture & Internals**](docs/architecture.md)
- [**Python API Reference**](docs/api.md)

Want to contribute or add a new backend? See [**CONTRIBUTING.md**](CONTRIBUTING.md).

## License

Licensed under the [Apache License, Version 2.0](LICENSE).
