MailToolsBox 0.1.0.4__tar.gz → 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,218 @@
1
+ import smtplib
2
+ from email.mime.text import MIMEText
3
+ from email.mime.multipart import MIMEMultipart
4
+ from email.mime.application import MIMEApplication
5
+ from email.utils import COMMASPACE, formatdate
6
+ from jinja2 import Template, Environment, FileSystemLoader, select_autoescape
7
+ from typing import List, Optional, Iterable
8
+ from pathlib import Path
9
+ import logging
10
+ from ssl import create_default_context
11
+ from email_validator import validate_email, EmailNotValidError
12
+
13
+ # Set up logging
14
+ logger = logging.getLogger(__name__)
15
+ logging.basicConfig(level=logging.INFO)
16
+
17
+
18
+ class EmailSender:
19
+ """Modern email sender with sync/async support and enhanced features."""
20
+
21
+ def __init__(
22
+ self,
23
+ user_email: str,
24
+ server_smtp_address: str,
25
+ user_email_password: str,
26
+ port: int = 587,
27
+ timeout: int = 10,
28
+ validate_emails: bool = True
29
+ ) -> None:
30
+ self.user_email = self._validate_email(user_email) if validate_emails else user_email
31
+ self.user_email_password = user_email_password
32
+ self.server_smtp_address = server_smtp_address
33
+ self.port = port
34
+ self.timeout = timeout
35
+ self.validate_emails = validate_emails
36
+ self.template_env = Environment(
37
+ loader=FileSystemLoader('templates'),
38
+ autoescape=select_autoescape(['html', 'xml'])
39
+ )
40
+ self.ssl_context = create_default_context()
41
+
42
+ def _validate_email(self, email_address: str) -> str:
43
+ """Validate and normalize email address using email-validator."""
44
+ try:
45
+ result = validate_email(email_address, check_deliverability=False)
46
+ return result.normalized
47
+ except EmailNotValidError as e:
48
+ logger.error(f"Invalid email address: {email_address}")
49
+ raise ValueError(f"Invalid email address: {email_address}") from e
50
+
51
+ def _create_base_message(
52
+ self,
53
+ subject: str,
54
+ recipients: Iterable[str],
55
+ cc: Optional[Iterable[str]] = None,
56
+ bcc: Optional[Iterable[str]] = None
57
+ ) -> MIMEMultipart:
58
+ """Create MIME message with proper headers."""
59
+ msg = MIMEMultipart()
60
+ msg['From'] = self.user_email
61
+ msg['Subject'] = subject
62
+ msg['Date'] = formatdate(localtime=True)
63
+
64
+ if self.validate_emails:
65
+ recipients = [self._validate_email(r) for r in recipients]
66
+
67
+ msg['To'] = COMMASPACE.join(recipients)
68
+
69
+ if cc:
70
+ validated_cc = [self._validate_email(c) for c in cc] if self.validate_emails else cc
71
+ msg['Cc'] = COMMASPACE.join(validated_cc)
72
+
73
+ if bcc:
74
+ validated_bcc = [self._validate_email(b) for b in bcc] if self.validate_emails else bcc
75
+ msg['Bcc'] = COMMASPACE.join(validated_bcc)
76
+
77
+ return msg
78
+
79
+ def _add_attachments(self, msg: MIMEMultipart, attachments: Iterable[str]) -> None:
80
+ """Add multiple attachments to the message."""
81
+ for file_path in attachments:
82
+ path = Path(file_path)
83
+ if not path.exists():
84
+ logger.warning(f"Attachment not found: {file_path}")
85
+ continue
86
+
87
+ with open(path, 'rb') as f:
88
+ part = MIMEApplication(
89
+ f.read(),
90
+ Name=path.name
91
+ )
92
+ part['Content-Disposition'] = f'attachment; filename="{path.name}"'
93
+ msg.attach(part)
94
+
95
+
96
+ def send(
97
+ self,
98
+ recipients: Iterable[str],
99
+ subject: str,
100
+ message_body: str,
101
+ cc: Optional[Iterable[str]] = None,
102
+ bcc: Optional[Iterable[str]] = None,
103
+ attachments: Optional[Iterable[str]] = None,
104
+ use_tls: bool = True,
105
+ html: bool = False
106
+ ) -> None:
107
+ """Synchronous email sending with improved error handling."""
108
+ msg = self._create_base_message(subject, recipients, cc, bcc)
109
+ msg.attach(MIMEText(message_body, 'html' if html else 'plain'))
110
+
111
+ if attachments:
112
+ self._add_attachments(msg, attachments)
113
+
114
+ try:
115
+ with smtplib.SMTP(self.server_smtp_address, self.port, timeout=self.timeout) as server:
116
+ if use_tls:
117
+ server.starttls(context=self.ssl_context)
118
+ server.login(self.user_email, self.user_email_password)
119
+ server.send_message(msg)
120
+ except smtplib.SMTPException as e:
121
+ logger.error(f"SMTP error occurred: {str(e)}")
122
+ raise
123
+ except Exception as e:
124
+ logger.error(f"Unexpected error: {str(e)}")
125
+ raise
126
+
127
+ def send_template(
128
+ self,
129
+ recipient: str,
130
+ subject: str,
131
+ template_name: str,
132
+ context: dict,
133
+ cc: Optional[Iterable[str]] = None,
134
+ attachments: Optional[Iterable[str]] = None,
135
+ use_tls: bool = True
136
+ ) -> None:
137
+ """Send email using Jinja2 template with autoescaping."""
138
+ template = self.template_env.get_template(template_name)
139
+ html_content = template.render(**context)
140
+ self.send([recipient], subject, html_content, cc=cc, attachments=attachments, use_tls=use_tls, html=True)
141
+
142
+
143
+
144
+ # Backward compatibility layer
145
+ class SendAgent(EmailSender):
146
+ """Legacy compatibility layer maintaining original interface."""
147
+
148
+ def send_mail(
149
+ self,
150
+ recipient_email: Optional[List[str]],
151
+ subject: str,
152
+ message_body: str,
153
+ cc: Optional[List[str]] = None,
154
+ bcc: Optional[List[str]] = None,
155
+ attachments: Optional[List[str]] = None,
156
+ tls: bool = True,
157
+ server_quit: bool = False
158
+ ) -> None:
159
+ logger.warning("SendAgent is deprecated, use EmailSender instead")
160
+
161
+ # Convert parameters to new format
162
+ self.send(
163
+ recipients=recipient_email,
164
+ subject=subject,
165
+ message_body=message_body,
166
+ cc=cc,
167
+ bcc=bcc,
168
+ attachments=attachments,
169
+ use_tls=tls
170
+ )
171
+
172
+ if server_quit:
173
+ self.server.quit()
174
+
175
+ def send_mail_with_template(
176
+ self,
177
+ recipient_email: str,
178
+ subject: str,
179
+ template_path: str,
180
+ template_vars: dict,
181
+ cc: Optional[List[str]] = None,
182
+ attachments: Optional[List[str]] = None,
183
+ tls: bool = True,
184
+ server_quit: bool = False
185
+ ) -> None:
186
+ logger.warning("send_mail_with_template is deprecated, use send_template instead")
187
+
188
+ self.send_template(
189
+ recipient=recipient_email,
190
+ subject=subject,
191
+ template_name=template_path,
192
+ context=template_vars,
193
+ cc=cc,
194
+ attachments=attachments,
195
+ use_tls=tls
196
+ )
197
+
198
+ if server_quit:
199
+ self.server.quit()
200
+
201
+
202
+ # Example usage
203
+ if __name__ == "__main__":
204
+ sender = EmailSender(
205
+ user_email="your@email.com",
206
+ server_smtp_address="smtp.example.com",
207
+ user_email_password="password",
208
+ port=587
209
+ )
210
+
211
+ # Sync send
212
+ sender.send(
213
+ recipients=["gh.rambod@gmail.com"],
214
+ subject="Modern Email",
215
+ message_body="<h1>HTML Content</h1>",
216
+ html=True,
217
+ attachments=["important.pdf"]
218
+ )
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.2
2
+ Name: MailToolsBox
3
+ Version: 1.0.0
4
+ Summary: A modern and efficient Python library for sending emails with SMTP, Jinja2 templates, and attachments.
5
+ Home-page: https://github.com/rambod/MailToolsBox
6
+ Download-URL: https://github.com/rambod/MailToolsBox/archive/refs/tags/v1.0.0.tar.gz
7
+ Author: Rambod Ghashghai
8
+ Author-email: gh.rambod@gmail.com
9
+ License: MIT
10
+ Keywords: Mail,SMTP,email,tools,attachments,Jinja2,Python,email-validation
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Communications :: Email
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Requires-Python: >=3.7
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE.txt
25
+ Requires-Dist: Jinja2>=3.0.2
26
+ Requires-Dist: email-validator>=2.0.0
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
32
+ Dynamic: download-url
33
+ Dynamic: home-page
34
+ Dynamic: keywords
35
+ Dynamic: license
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
39
+
40
+ # MailToolsBox
41
+
42
+ MailToolsBox is a modern, feature-rich Python package designed for sending and managing emails with ease. It provides robust functionality for handling SMTP email sending, template-based emails using Jinja2, attachments, CC/BCC support, and email validation. Additionally, MailToolsBox ensures backward compatibility with legacy implementations.
43
+
44
+ ## Features
45
+
46
+ - **Send emails via SMTP with ease**
47
+ - **Support for multiple recipients (To, CC, BCC)**
48
+ - **HTML and plain text email support**
49
+ - **Attachment handling**
50
+ - **Template-based email rendering using Jinja2**
51
+ - **Secure email transactions with TLS/SSL**
52
+ - **Email address validation**
53
+ - **Logging for debugging and monitoring**
54
+ - **Backward compatibility with `SendAgent`**
55
+
56
+ ---
57
+
58
+ ## Installation
59
+
60
+ Install MailToolsBox from PyPI using pip:
61
+
62
+ ```bash
63
+ pip install MailToolsBox
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Getting Started
69
+
70
+ ### 1. **Sending a Basic Email**
71
+
72
+ The `EmailSender` class is the primary interface for sending emails. Below is an example of sending a simple plain text email:
73
+
74
+ ```python
75
+ from MailToolsBox import EmailSender
76
+
77
+ # Email configuration
78
+ sender = EmailSender(
79
+ user_email="your@email.com",
80
+ server_smtp_address="smtp.example.com",
81
+ user_email_password="yourpassword",
82
+ port=587
83
+ )
84
+
85
+ # Sending email
86
+ sender.send(
87
+ recipients=["recipient@example.com"],
88
+ subject="Test Email",
89
+ message_body="Hello, this is a test email!"
90
+ )
91
+ ```
92
+
93
+ ---
94
+
95
+ ### 2. **Sending an HTML Email with Attachments**
96
+
97
+ ```python
98
+ sender.send(
99
+ recipients=["recipient@example.com"],
100
+ subject="HTML Email Example",
101
+ message_body="""<h1>Welcome!</h1><p>This is an <strong>HTML email</strong>.</p>""",
102
+
103
+ html=True,
104
+ attachments=["/path/to/document.pdf"]
105
+ )
106
+ ```
107
+
108
+ ---
109
+
110
+ ### 3. **Using Email Templates (Jinja2)**
111
+
112
+ MailToolsBox allows sending emails using Jinja2 templates stored in the `templates/` directory.
113
+
114
+ #### **Example Template (`templates/welcome.html`)**:
115
+
116
+ ```html
117
+ <html>
118
+ <head>
119
+ <title>Welcome</title>
120
+ </head>
121
+ <body>
122
+ <h1>Welcome, {{ username }}!</h1>
123
+ <p>Click <a href="{{ activation_link }}">here</a> to activate your account.</p>
124
+ </body>
125
+ </html>
126
+ ```
127
+
128
+ #### **Sending an Email with a Template**:
129
+
130
+ ```python
131
+ context = {
132
+ "username": "John Doe",
133
+ "activation_link": "https://example.com/activate"
134
+ }
135
+
136
+ sender.send_template(
137
+ recipient="recipient@example.com",
138
+ subject="Welcome to Our Service",
139
+ template_name="welcome.html",
140
+ context=context
141
+ )
142
+ ```
143
+
144
+ ---
145
+
146
+ ### 4. **CC, BCC, and Multiple Recipients**
147
+
148
+ ```python
149
+ sender.send(
150
+ recipients=["recipient@example.com"],
151
+ subject="CC & BCC Example",
152
+ message_body="This email has CC and BCC recipients!",
153
+ cc=["cc@example.com"],
154
+ bcc=["bcc@example.com"]
155
+ )
156
+ ```
157
+
158
+ ---
159
+
160
+ ### 5. **Backward Compatibility with `SendAgent`**
161
+
162
+ For those migrating from earlier versions, `SendAgent` ensures seamless compatibility:
163
+
164
+ ```python
165
+ from MailToolsBox import SendAgent
166
+
167
+ legacy_sender = SendAgent(
168
+ user_email="your@email.com",
169
+ server_smtp_address="smtp.example.com",
170
+ user_email_password="yourpassword",
171
+ port=587
172
+ )
173
+
174
+ legacy_sender.send_mail(
175
+ recipient_email=["recipient@example.com"],
176
+ subject="Legacy Compatibility Test",
177
+ message_body="Testing backward compatibility."
178
+ )
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Configuration & Security Best Practices
184
+
185
+ - **Use environment variables** instead of hardcoding credentials.
186
+ - **Enable 2FA** on your email provider and use app passwords if required.
187
+ - **Use TLS/SSL** to ensure secure email delivery.
188
+
189
+ Example using environment variables:
190
+
191
+ ```python
192
+ import os
193
+
194
+ sender = EmailSender(
195
+ user_email=os.getenv("EMAIL"),
196
+ server_smtp_address=os.getenv("SMTP_SERVER"),
197
+ user_email_password=os.getenv("EMAIL_PASSWORD"),
198
+ port=int(os.getenv("SMTP_PORT", 587))
199
+ )
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Error Handling & Logging
205
+
206
+ MailToolsBox provides built-in logging to help debug issues:
207
+
208
+ ```python
209
+ import logging
210
+ logging.basicConfig(level=logging.INFO)
211
+ ```
212
+
213
+ Example of handling exceptions:
214
+
215
+ ```python
216
+ try:
217
+ sender.send(
218
+ recipients=["recipient@example.com"],
219
+ subject="Error Handling Test",
220
+ message_body="This is a test email."
221
+ )
222
+ except Exception as e:
223
+ print(f"Failed to send email: {e}")
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Contributing
229
+
230
+ MailToolsBox is an open-source project. Contributions are welcome! To contribute:
231
+
232
+ 1. Fork the repository on GitHub.
233
+ 2. Create a new feature branch.
234
+ 3. Implement changes and write tests.
235
+ 4. Submit a pull request for review.
236
+
237
+ For discussions, visit **[rambod.net](https://www.rambod.net)**.
238
+
239
+ ---
240
+
241
+ ## License
242
+
243
+ MailToolsBox is licensed under the MIT License. See the [LICENSE](https://choosealicense.com/licenses/mit/) for details.
@@ -0,0 +1,2 @@
1
+ Jinja2>=3.0.2
2
+ email-validator>=2.0.0
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.2
2
+ Name: MailToolsBox
3
+ Version: 1.0.0
4
+ Summary: A modern and efficient Python library for sending emails with SMTP, Jinja2 templates, and attachments.
5
+ Home-page: https://github.com/rambod/MailToolsBox
6
+ Download-URL: https://github.com/rambod/MailToolsBox/archive/refs/tags/v1.0.0.tar.gz
7
+ Author: Rambod Ghashghai
8
+ Author-email: gh.rambod@gmail.com
9
+ License: MIT
10
+ Keywords: Mail,SMTP,email,tools,attachments,Jinja2,Python,email-validation
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Communications :: Email
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Requires-Python: >=3.7
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE.txt
25
+ Requires-Dist: Jinja2>=3.0.2
26
+ Requires-Dist: email-validator>=2.0.0
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
32
+ Dynamic: download-url
33
+ Dynamic: home-page
34
+ Dynamic: keywords
35
+ Dynamic: license
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
39
+
40
+ # MailToolsBox
41
+
42
+ MailToolsBox is a modern, feature-rich Python package designed for sending and managing emails with ease. It provides robust functionality for handling SMTP email sending, template-based emails using Jinja2, attachments, CC/BCC support, and email validation. Additionally, MailToolsBox ensures backward compatibility with legacy implementations.
43
+
44
+ ## Features
45
+
46
+ - **Send emails via SMTP with ease**
47
+ - **Support for multiple recipients (To, CC, BCC)**
48
+ - **HTML and plain text email support**
49
+ - **Attachment handling**
50
+ - **Template-based email rendering using Jinja2**
51
+ - **Secure email transactions with TLS/SSL**
52
+ - **Email address validation**
53
+ - **Logging for debugging and monitoring**
54
+ - **Backward compatibility with `SendAgent`**
55
+
56
+ ---
57
+
58
+ ## Installation
59
+
60
+ Install MailToolsBox from PyPI using pip:
61
+
62
+ ```bash
63
+ pip install MailToolsBox
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Getting Started
69
+
70
+ ### 1. **Sending a Basic Email**
71
+
72
+ The `EmailSender` class is the primary interface for sending emails. Below is an example of sending a simple plain text email:
73
+
74
+ ```python
75
+ from MailToolsBox import EmailSender
76
+
77
+ # Email configuration
78
+ sender = EmailSender(
79
+ user_email="your@email.com",
80
+ server_smtp_address="smtp.example.com",
81
+ user_email_password="yourpassword",
82
+ port=587
83
+ )
84
+
85
+ # Sending email
86
+ sender.send(
87
+ recipients=["recipient@example.com"],
88
+ subject="Test Email",
89
+ message_body="Hello, this is a test email!"
90
+ )
91
+ ```
92
+
93
+ ---
94
+
95
+ ### 2. **Sending an HTML Email with Attachments**
96
+
97
+ ```python
98
+ sender.send(
99
+ recipients=["recipient@example.com"],
100
+ subject="HTML Email Example",
101
+ message_body="""<h1>Welcome!</h1><p>This is an <strong>HTML email</strong>.</p>""",
102
+
103
+ html=True,
104
+ attachments=["/path/to/document.pdf"]
105
+ )
106
+ ```
107
+
108
+ ---
109
+
110
+ ### 3. **Using Email Templates (Jinja2)**
111
+
112
+ MailToolsBox allows sending emails using Jinja2 templates stored in the `templates/` directory.
113
+
114
+ #### **Example Template (`templates/welcome.html`)**:
115
+
116
+ ```html
117
+ <html>
118
+ <head>
119
+ <title>Welcome</title>
120
+ </head>
121
+ <body>
122
+ <h1>Welcome, {{ username }}!</h1>
123
+ <p>Click <a href="{{ activation_link }}">here</a> to activate your account.</p>
124
+ </body>
125
+ </html>
126
+ ```
127
+
128
+ #### **Sending an Email with a Template**:
129
+
130
+ ```python
131
+ context = {
132
+ "username": "John Doe",
133
+ "activation_link": "https://example.com/activate"
134
+ }
135
+
136
+ sender.send_template(
137
+ recipient="recipient@example.com",
138
+ subject="Welcome to Our Service",
139
+ template_name="welcome.html",
140
+ context=context
141
+ )
142
+ ```
143
+
144
+ ---
145
+
146
+ ### 4. **CC, BCC, and Multiple Recipients**
147
+
148
+ ```python
149
+ sender.send(
150
+ recipients=["recipient@example.com"],
151
+ subject="CC & BCC Example",
152
+ message_body="This email has CC and BCC recipients!",
153
+ cc=["cc@example.com"],
154
+ bcc=["bcc@example.com"]
155
+ )
156
+ ```
157
+
158
+ ---
159
+
160
+ ### 5. **Backward Compatibility with `SendAgent`**
161
+
162
+ For those migrating from earlier versions, `SendAgent` ensures seamless compatibility:
163
+
164
+ ```python
165
+ from MailToolsBox import SendAgent
166
+
167
+ legacy_sender = SendAgent(
168
+ user_email="your@email.com",
169
+ server_smtp_address="smtp.example.com",
170
+ user_email_password="yourpassword",
171
+ port=587
172
+ )
173
+
174
+ legacy_sender.send_mail(
175
+ recipient_email=["recipient@example.com"],
176
+ subject="Legacy Compatibility Test",
177
+ message_body="Testing backward compatibility."
178
+ )
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Configuration & Security Best Practices
184
+
185
+ - **Use environment variables** instead of hardcoding credentials.
186
+ - **Enable 2FA** on your email provider and use app passwords if required.
187
+ - **Use TLS/SSL** to ensure secure email delivery.
188
+
189
+ Example using environment variables:
190
+
191
+ ```python
192
+ import os
193
+
194
+ sender = EmailSender(
195
+ user_email=os.getenv("EMAIL"),
196
+ server_smtp_address=os.getenv("SMTP_SERVER"),
197
+ user_email_password=os.getenv("EMAIL_PASSWORD"),
198
+ port=int(os.getenv("SMTP_PORT", 587))
199
+ )
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Error Handling & Logging
205
+
206
+ MailToolsBox provides built-in logging to help debug issues:
207
+
208
+ ```python
209
+ import logging
210
+ logging.basicConfig(level=logging.INFO)
211
+ ```
212
+
213
+ Example of handling exceptions:
214
+
215
+ ```python
216
+ try:
217
+ sender.send(
218
+ recipients=["recipient@example.com"],
219
+ subject="Error Handling Test",
220
+ message_body="This is a test email."
221
+ )
222
+ except Exception as e:
223
+ print(f"Failed to send email: {e}")
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Contributing
229
+
230
+ MailToolsBox is an open-source project. Contributions are welcome! To contribute:
231
+
232
+ 1. Fork the repository on GitHub.
233
+ 2. Create a new feature branch.
234
+ 3. Implement changes and write tests.
235
+ 4. Submit a pull request for review.
236
+
237
+ For discussions, visit **[rambod.net](https://www.rambod.net)**.
238
+
239
+ ---
240
+
241
+ ## License
242
+
243
+ MailToolsBox is licensed under the MIT License. See the [LICENSE](https://choosealicense.com/licenses/mit/) for details.