MailToolsBox


NameMailToolsBox JSON
Version 1.1.0 PyPI version JSON
download
home_pagehttps://github.com/rambod/MailToolsBox
SummaryModern sync and async SMTP with optional TLS/SSL, OAuth2 XOAUTH2, Jinja2 templates, and attachments.
upload_time2025-08-30 16:34:16
maintainerNone
docs_urlNone
authorRambod Ghashghai
requires_python>=3.8
licenseMIT
keywords mail smtp email tls ssl oauth2 xoauth2 jinja2 asyncio aiosmtplib email-validation
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # MailToolsBox

MailToolsBox is a modern, pragmatic email toolkit for Python. It gives you clean, production‑grade SMTP sending and a capable IMAP client in one package. The design favors explicit security controls, sane defaults, and simple APIs that scale from quick scripts to services.

---

## What you get

- SMTP sender with sync and async APIs
- IMAP client with search, fetch, flags, move, delete, export
- Security modes: `auto`, `starttls`, `ssl`, `none`
- Optional OAuth2 XOAUTH2 for both SMTP and IMAP
- Gmail and Exchange Online presets
- Jinja2 templates with auto plain text fallback
- MIME smart attachment handling
- Bulk sending helpers
- Email validation
- Environment variable configuration
- Backward compatibility shim `SendAgent`

---

## Install

```bash
pip install MailToolsBox
```

---

## Quick start

### Send a basic email

```python
from MailToolsBox import EmailSender

sender = EmailSender(
    user_email="you@example.com",
    server_smtp_address="smtp.example.com",
    user_email_password="password",
    port=587,                        # typical for STARTTLS
    security_mode="starttls"         # or "auto"
)

sender.send(
    recipients=["to@example.com"],
    subject="Hello",
    message_body="Plain text body"
)
```

### Read emails

```python
from MailToolsBox.imap_client import ImapClient  # or the path you placed it under

with ImapClient(
    email_account="you@example.com",
    password="password",
    server_address="imap.example.com",
    port=993,
    security_mode="ssl"
) as imap:
    imap.select("INBOX")
    uids = imap.search("UNSEEN")
    messages = imap.fetch_many(uids[:10])
    for m in messages:
        print(m.subject, m.from_[0].email if m.from_ else None)
```

> Tip: If you installed the package as a single module, import paths may be `from MailToolsBox import ImapClient`. Keep them consistent with your package layout.

---

## SMTP in depth

### Security modes

- `auto`:
  - If port is 465 use implicit SSL.
  - Otherwise attempt STARTTLS if the server advertises it. If not available, stay plain.
- `starttls`: force STARTTLS upgrade.
- `ssl`: implicit SSL on connect, typical for port 465.
- `none`: no TLS. Use only inside trusted networks.

### Gmail and Exchange recipes

```python
# Gmail with app password
sender = EmailSender.for_gmail_app_password("you@gmail.com", "abcd abcd abcd abcd")
sender.send(["to@example.com"], "Hi", "Body")

# Exchange Online with SMTP AUTH
exchange = EmailSender.for_exchange_smtp_auth("you@company.com", "password")
exchange.send(["person@company.com"], "Status", "Body")
```

### OAuth2 XOAUTH2

```python
oauth_sender = EmailSender(
    user_email="you@gmail.com",
    server_smtp_address="smtp.gmail.com",
    port=587,
    security_mode="starttls",
    oauth2_access_token="ya29.a0Af..."  # obtain via your OAuth flow
)
oauth_sender.send(["to@example.com"], "OAuth2", "Sent with XOAUTH2")
```

### HTML with plain fallback and attachments

```python
html = "<h1>Report</h1><p>See attachment.</p>"
sender.send(
    recipients=["to@example.com"],
    subject="Monthly report",
    message_body=html,
    html=True,
    attachments=["/path/report.pdf", "/path/chart.png"]
)
```

### Async sending

```python
import asyncio

async def main():
    await sender.send_async(
        recipients=["to@example.com"],
        subject="Async",
        message_body="Non blocking send"
    )

asyncio.run(main())
```

### Bulk helpers

```python
sender.send_bulk(
    recipients=["a@example.com", "b@example.com"],
    subject="Announcement",
    message_body="Sent individually to protect privacy"
)
```

### Environment variables

```bash
export EMAIL=you@example.com
export EMAIL_PASSWORD=apppass
export SMTP_SERVER=smtp.gmail.com
export SMTP_PORT=465
export EMAIL_SECURITY=ssl
export EMAIL_REPLY_TO=noreply@example.com
```

```python
sender = EmailSender.from_env()
```

---

## IMAP in depth

The `ImapClient` provides safe defaults with flexible control when you need it.

### Connect and select

```python
imap = ImapClient(
    email_account="you@example.com",
    password="password",
    server_address="imap.example.com",
    port=993,
    security_mode="ssl"
)
imap.login()
imap.select("INBOX")
```

Or use context manager:

```python
with ImapClient.from_env() as imap:
    imap.select("INBOX")
    print(imap.list_mailboxes())
```

Environment variables:

```bash
export IMAP_EMAIL=you@example.com
export IMAP_PASSWORD=apppass
export IMAP_SERVER=imap.gmail.com
export IMAP_PORT=993
export IMAP_SECURITY=ssl
# Optional OAuth token
export IMAP_OAUTH2_TOKEN=ya29.a0Af...
```

### Search and fetch

```python
uids = imap.search("UNSEEN", "SINCE", "01-Jan-2025")
item = imap.fetch(uids[0])
print(item.subject, item.date, item.flags)
print(item.text or item.html)
```

### Attachments and export

```python
paths = imap.save_attachments(item, "./attachments")
eml_path = imap.save_eml(item, "./message.eml")
```

### Flags, move, delete

```python
imap.mark_seen(item.uid)
imap.add_flags(item.uid, "\Flagged")
imap.move([item.uid], "Archive")
imap.delete(item.uid)
imap.expunge()
```

### Legacy style exports

```python
# Dump mailbox to one text file
imap.download_mail_text(path="./dumps", mailbox="INBOX")

# Export selected emails as JSON
imap.download_mail_json(lookup="UNSEEN", save=True, path="./dumps", file_name="mail.json")

# Save each message to .eml
imap.download_mail_eml(directory="./eml", lookup="ALL", mailbox="INBOX")
```

### OAuth2 XOAUTH2

```python
imap = ImapClient(
    email_account="you@gmail.com",
    password=None,
    server_address="imap.gmail.com",
    port=993,
    security_mode="ssl",
    oauth2_access_token="ya29.a0Af..."
)
with imap:
    imap.select("INBOX")
    uids = imap.search("ALL")
```

---

## Validation and templates

- Addresses are normalized with `email-validator` when validation is enabled.
- Templates use Jinja2 with autoescape for HTML and XML.
- HTML sending includes a plain text alternative for better deliverability.

Template example `templates/welcome.html`:

```html
<h1>Welcome, {{ user }}</h1>
<p>Activate your account: <a href="{{ link }}">activate</a></p>
```

Send with template:

```python
sender = EmailSender(
    user_email="you@example.com",
    server_smtp_address="smtp.example.com",
    user_email_password="pw",
    template_dir="./templates"
)

sender.send_template(
    recipient="to@example.com",
    subject="Welcome",
    template_name="welcome.html",
    context={"user": "Alex", "link": "https://example.com/activate"}
)
```

---

## Backward compatibility

`SendAgent` stays available for older codebases. It is thin and delegates to `EmailSender`. Prefer `EmailSender` in new code.

```python
from MailToolsBox import SendAgent
legacy = SendAgent("you@example.com", "smtp.example.com", "pw", port=587)
legacy.send_mail(["to@example.com"], "Subject", "Body", tls=True)
```

---

## Security notes

- Prefer `ssl` on 465 or `starttls` on 587.
- Use app passwords when your provider offers them.
- Prefer OAuth2 tokens for long term services.
- Use `none` only on trusted networks.

---

## Troubleshooting

- Authentication errors on Gmail usually mean you need an app password or OAuth2.
- If a STARTTLS upgrade fails in `auto`, set `security_mode="ssl"` on 465 or `security_mode="starttls"` on 587.
- For corporate relays that do not support TLS, set `security_mode="none"` and ensure the network is trusted.
- Enable logging in your application to capture SMTP or IMAP server responses.

```python
import logging
logging.basicConfig(level=logging.INFO)
```

---

## Contributing

PRs are welcome. Keep changes focused and covered with tests. Add docs for new behavior. Use ruff and black for formatting.

---

## License

MIT. See LICENSE for details.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/rambod/MailToolsBox",
    "name": "MailToolsBox",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "mail, smtp, email, tls, ssl, oauth2, xoauth2, jinja2, asyncio, aiosmtplib, email-validation",
    "author": "Rambod Ghashghai",
    "author_email": "gh.rambod@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/54/f0/73572eef160046110b7dba78f7bd6f0b84c47149c8b97bc6248584fe06d5/mailtoolsbox-1.1.0.tar.gz",
    "platform": null,
    "description": "# MailToolsBox\n\nMailToolsBox is a modern, pragmatic email toolkit for Python. It gives you clean, production\u2011grade SMTP sending and a capable IMAP client in one package. The design favors explicit security controls, sane defaults, and simple APIs that scale from quick scripts to services.\n\n---\n\n## What you get\n\n- SMTP sender with sync and async APIs\n- IMAP client with search, fetch, flags, move, delete, export\n- Security modes: `auto`, `starttls`, `ssl`, `none`\n- Optional OAuth2 XOAUTH2 for both SMTP and IMAP\n- Gmail and Exchange Online presets\n- Jinja2 templates with auto plain text fallback\n- MIME smart attachment handling\n- Bulk sending helpers\n- Email validation\n- Environment variable configuration\n- Backward compatibility shim `SendAgent`\n\n---\n\n## Install\n\n```bash\npip install MailToolsBox\n```\n\n---\n\n## Quick start\n\n### Send a basic email\n\n```python\nfrom MailToolsBox import EmailSender\n\nsender = EmailSender(\n    user_email=\"you@example.com\",\n    server_smtp_address=\"smtp.example.com\",\n    user_email_password=\"password\",\n    port=587,                        # typical for STARTTLS\n    security_mode=\"starttls\"         # or \"auto\"\n)\n\nsender.send(\n    recipients=[\"to@example.com\"],\n    subject=\"Hello\",\n    message_body=\"Plain text body\"\n)\n```\n\n### Read emails\n\n```python\nfrom MailToolsBox.imap_client import ImapClient  # or the path you placed it under\n\nwith ImapClient(\n    email_account=\"you@example.com\",\n    password=\"password\",\n    server_address=\"imap.example.com\",\n    port=993,\n    security_mode=\"ssl\"\n) as imap:\n    imap.select(\"INBOX\")\n    uids = imap.search(\"UNSEEN\")\n    messages = imap.fetch_many(uids[:10])\n    for m in messages:\n        print(m.subject, m.from_[0].email if m.from_ else None)\n```\n\n> Tip: If you installed the package as a single module, import paths may be `from MailToolsBox import ImapClient`. Keep them consistent with your package layout.\n\n---\n\n## SMTP in depth\n\n### Security modes\n\n- `auto`:\n  - If port is 465 use implicit SSL.\n  - Otherwise attempt STARTTLS if the server advertises it. If not available, stay plain.\n- `starttls`: force STARTTLS upgrade.\n- `ssl`: implicit SSL on connect, typical for port 465.\n- `none`: no TLS. Use only inside trusted networks.\n\n### Gmail and Exchange recipes\n\n```python\n# Gmail with app password\nsender = EmailSender.for_gmail_app_password(\"you@gmail.com\", \"abcd abcd abcd abcd\")\nsender.send([\"to@example.com\"], \"Hi\", \"Body\")\n\n# Exchange Online with SMTP AUTH\nexchange = EmailSender.for_exchange_smtp_auth(\"you@company.com\", \"password\")\nexchange.send([\"person@company.com\"], \"Status\", \"Body\")\n```\n\n### OAuth2 XOAUTH2\n\n```python\noauth_sender = EmailSender(\n    user_email=\"you@gmail.com\",\n    server_smtp_address=\"smtp.gmail.com\",\n    port=587,\n    security_mode=\"starttls\",\n    oauth2_access_token=\"ya29.a0Af...\"  # obtain via your OAuth flow\n)\noauth_sender.send([\"to@example.com\"], \"OAuth2\", \"Sent with XOAUTH2\")\n```\n\n### HTML with plain fallback and attachments\n\n```python\nhtml = \"<h1>Report</h1><p>See attachment.</p>\"\nsender.send(\n    recipients=[\"to@example.com\"],\n    subject=\"Monthly report\",\n    message_body=html,\n    html=True,\n    attachments=[\"/path/report.pdf\", \"/path/chart.png\"]\n)\n```\n\n### Async sending\n\n```python\nimport asyncio\n\nasync def main():\n    await sender.send_async(\n        recipients=[\"to@example.com\"],\n        subject=\"Async\",\n        message_body=\"Non blocking send\"\n    )\n\nasyncio.run(main())\n```\n\n### Bulk helpers\n\n```python\nsender.send_bulk(\n    recipients=[\"a@example.com\", \"b@example.com\"],\n    subject=\"Announcement\",\n    message_body=\"Sent individually to protect privacy\"\n)\n```\n\n### Environment variables\n\n```bash\nexport EMAIL=you@example.com\nexport EMAIL_PASSWORD=apppass\nexport SMTP_SERVER=smtp.gmail.com\nexport SMTP_PORT=465\nexport EMAIL_SECURITY=ssl\nexport EMAIL_REPLY_TO=noreply@example.com\n```\n\n```python\nsender = EmailSender.from_env()\n```\n\n---\n\n## IMAP in depth\n\nThe `ImapClient` provides safe defaults with flexible control when you need it.\n\n### Connect and select\n\n```python\nimap = ImapClient(\n    email_account=\"you@example.com\",\n    password=\"password\",\n    server_address=\"imap.example.com\",\n    port=993,\n    security_mode=\"ssl\"\n)\nimap.login()\nimap.select(\"INBOX\")\n```\n\nOr use context manager:\n\n```python\nwith ImapClient.from_env() as imap:\n    imap.select(\"INBOX\")\n    print(imap.list_mailboxes())\n```\n\nEnvironment variables:\n\n```bash\nexport IMAP_EMAIL=you@example.com\nexport IMAP_PASSWORD=apppass\nexport IMAP_SERVER=imap.gmail.com\nexport IMAP_PORT=993\nexport IMAP_SECURITY=ssl\n# Optional OAuth token\nexport IMAP_OAUTH2_TOKEN=ya29.a0Af...\n```\n\n### Search and fetch\n\n```python\nuids = imap.search(\"UNSEEN\", \"SINCE\", \"01-Jan-2025\")\nitem = imap.fetch(uids[0])\nprint(item.subject, item.date, item.flags)\nprint(item.text or item.html)\n```\n\n### Attachments and export\n\n```python\npaths = imap.save_attachments(item, \"./attachments\")\neml_path = imap.save_eml(item, \"./message.eml\")\n```\n\n### Flags, move, delete\n\n```python\nimap.mark_seen(item.uid)\nimap.add_flags(item.uid, \"\\Flagged\")\nimap.move([item.uid], \"Archive\")\nimap.delete(item.uid)\nimap.expunge()\n```\n\n### Legacy style exports\n\n```python\n# Dump mailbox to one text file\nimap.download_mail_text(path=\"./dumps\", mailbox=\"INBOX\")\n\n# Export selected emails as JSON\nimap.download_mail_json(lookup=\"UNSEEN\", save=True, path=\"./dumps\", file_name=\"mail.json\")\n\n# Save each message to .eml\nimap.download_mail_eml(directory=\"./eml\", lookup=\"ALL\", mailbox=\"INBOX\")\n```\n\n### OAuth2 XOAUTH2\n\n```python\nimap = ImapClient(\n    email_account=\"you@gmail.com\",\n    password=None,\n    server_address=\"imap.gmail.com\",\n    port=993,\n    security_mode=\"ssl\",\n    oauth2_access_token=\"ya29.a0Af...\"\n)\nwith imap:\n    imap.select(\"INBOX\")\n    uids = imap.search(\"ALL\")\n```\n\n---\n\n## Validation and templates\n\n- Addresses are normalized with `email-validator` when validation is enabled.\n- Templates use Jinja2 with autoescape for HTML and XML.\n- HTML sending includes a plain text alternative for better deliverability.\n\nTemplate example `templates/welcome.html`:\n\n```html\n<h1>Welcome, {{ user }}</h1>\n<p>Activate your account: <a href=\"{{ link }}\">activate</a></p>\n```\n\nSend with template:\n\n```python\nsender = EmailSender(\n    user_email=\"you@example.com\",\n    server_smtp_address=\"smtp.example.com\",\n    user_email_password=\"pw\",\n    template_dir=\"./templates\"\n)\n\nsender.send_template(\n    recipient=\"to@example.com\",\n    subject=\"Welcome\",\n    template_name=\"welcome.html\",\n    context={\"user\": \"Alex\", \"link\": \"https://example.com/activate\"}\n)\n```\n\n---\n\n## Backward compatibility\n\n`SendAgent` stays available for older codebases. It is thin and delegates to `EmailSender`. Prefer `EmailSender` in new code.\n\n```python\nfrom MailToolsBox import SendAgent\nlegacy = SendAgent(\"you@example.com\", \"smtp.example.com\", \"pw\", port=587)\nlegacy.send_mail([\"to@example.com\"], \"Subject\", \"Body\", tls=True)\n```\n\n---\n\n## Security notes\n\n- Prefer `ssl` on 465 or `starttls` on 587.\n- Use app passwords when your provider offers them.\n- Prefer OAuth2 tokens for long term services.\n- Use `none` only on trusted networks.\n\n---\n\n## Troubleshooting\n\n- Authentication errors on Gmail usually mean you need an app password or OAuth2.\n- If a STARTTLS upgrade fails in `auto`, set `security_mode=\"ssl\"` on 465 or `security_mode=\"starttls\"` on 587.\n- For corporate relays that do not support TLS, set `security_mode=\"none\"` and ensure the network is trusted.\n- Enable logging in your application to capture SMTP or IMAP server responses.\n\n```python\nimport logging\nlogging.basicConfig(level=logging.INFO)\n```\n\n---\n\n## Contributing\n\nPRs are welcome. Keep changes focused and covered with tests. Add docs for new behavior. Use ruff and black for formatting.\n\n---\n\n## License\n\nMIT. See LICENSE for details.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Modern sync and async SMTP with optional TLS/SSL, OAuth2 XOAUTH2, Jinja2 templates, and attachments.",
    "version": "1.1.0",
    "project_urls": {
        "Download": "https://github.com/rambod/MailToolsBox/archive/refs/tags/v1.1.0.tar.gz",
        "Homepage": "https://github.com/rambod/MailToolsBox",
        "Issues": "https://github.com/rambod/MailToolsBox/issues",
        "Source": "https://github.com/rambod/MailToolsBox"
    },
    "split_keywords": [
        "mail",
        " smtp",
        " email",
        " tls",
        " ssl",
        " oauth2",
        " xoauth2",
        " jinja2",
        " asyncio",
        " aiosmtplib",
        " email-validation"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "49a6373e466744874b650a4dc70dd52529407edddf6c30018f15509257000365",
                "md5": "51ac039b793bca83046e6a1e8c62852c",
                "sha256": "ba0b318c61d9ad2d2c94a3c29dee90f15f5234e361a5d07e51420ad545accbac"
            },
            "downloads": -1,
            "filename": "mailtoolsbox-1.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "51ac039b793bca83046e6a1e8c62852c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 16159,
            "upload_time": "2025-08-30T16:34:14",
            "upload_time_iso_8601": "2025-08-30T16:34:14.945471Z",
            "url": "https://files.pythonhosted.org/packages/49/a6/373e466744874b650a4dc70dd52529407edddf6c30018f15509257000365/mailtoolsbox-1.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "54f073572eef160046110b7dba78f7bd6f0b84c47149c8b97bc6248584fe06d5",
                "md5": "6394ac566d48c9846b9ea7a1e469bf3b",
                "sha256": "0bad798cdc8b085e61214e28ff61567b681be72b1e82ec83f0dc067f7f0bbdf8"
            },
            "downloads": -1,
            "filename": "mailtoolsbox-1.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "6394ac566d48c9846b9ea7a1e469bf3b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 17973,
            "upload_time": "2025-08-30T16:34:16",
            "upload_time_iso_8601": "2025-08-30T16:34:16.575058Z",
            "url": "https://files.pythonhosted.org/packages/54/f0/73572eef160046110b7dba78f7bd6f0b84c47149c8b97bc6248584fe06d5/mailtoolsbox-1.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-30 16:34:16",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "rambod",
    "github_project": "MailToolsBox",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "mailtoolsbox"
}
        
Elapsed time: 2.44871s