# 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"
}