Last active
February 22, 2017 13:44
-
-
Save techouse/aabb266bc4ad3f27047b to your computer and use it in GitHub Desktop.
Since sendmail wasn't enough for me, I wrote a simple Python script that can send emails directly from a terminal shell. Requires the validators package (https://pypi.python.org/pypi/validators/).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
__author__ = "Klemen Tušar" | |
__email__ = "[email protected]" | |
__copyright__ = "GPL" | |
__version__ = "1.0.2" | |
__date__ = "2017-02-22" | |
__status__ = "Production" | |
import os, smtplib, validators | |
from email.message import EmailMessage | |
from email import charset | |
from email.headerregistry import Address | |
class SendMail: | |
def __init__(self, **kwargs): | |
self._from_address = kwargs.get('from_address', None) | |
self._from_name = kwargs.get('from_name', os.uname()[1]) | |
self._to_addresses = kwargs.get('to_addresses', tuple()) | |
self._subject = kwargs.get('subject', None) | |
self._body = kwargs.get('body', None) | |
self._is_html = kwargs.get('is_html', False) | |
charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8') | |
@property | |
def from_address(self): | |
return self._from_address | |
@from_address.setter | |
def from_address(self, from_address): | |
self._from_address = from_address | |
@from_address.deleter | |
def from_address(self): | |
self._from_address = None | |
@property | |
def from_name(self): | |
return self._from_name | |
@from_name.setter | |
def from_name(self, from_name): | |
self._from_name = from_name | |
@from_name.deleter | |
def from_name(self): | |
self._from_name = os.uname()[1] | |
@property | |
def to_addresses(self): | |
return self._to_address | |
@to_addresses.setter | |
def to_addresses(self, to_addresses): | |
if isinstance(to_addresses, (list, tuple)): | |
self._to_addresses = tuple(to_addresses) | |
else: | |
raise TypeError('To addresses must be either list or tuple') | |
@to_addresses.deleter | |
def to_addresses(self): | |
self._to_addresses = tuple() | |
@property | |
def subject(self): | |
return self._subject | |
@subject.setter | |
def subject(self, subject): | |
self._subject = subject | |
@subject.deleter | |
def subject(self): | |
self._subject = None | |
@property | |
def body(self): | |
return self._body | |
@body.setter | |
def body(self, body): | |
self._body = body | |
@body.deleter | |
def body(self): | |
self._body = None | |
@property | |
def is_html(self): | |
return self._is_html | |
@is_html.setter | |
def is_html(self, is_html): | |
self._is_html = is_html | |
@is_html.deleter | |
def is_html(self): | |
self._is_html = False | |
def _validate_data(self): | |
if not self._from_address: | |
raise ValueError('Please supply a from address') | |
elif not isinstance(self._from_address, str): | |
raise TypeError('From address must be of type str') | |
elif not validators.email(self._from_address): | |
raise ValueError('Invalid from address') | |
elif not self._to_addresses: | |
raise ValueError('Please supply a to address') | |
elif not isinstance(self._to_addresses, (list, tuple)): | |
raise TypeError('To address must be of type str') | |
elif not self._subject: | |
raise ValueError('Please supply a subject') | |
elif not isinstance(self._subject, str): | |
raise TypeError('Subject must be of type str') | |
elif not self._body: | |
raise ValueError('Please supply a body') | |
elif not isinstance(self._body, str): | |
raise TypeError('Body must be of type str') | |
else: | |
for to_address in self._to_addresses: | |
if not validators.email(to_address): | |
raise ValueError('invalid to address: {}'.format(to_address)) | |
def _build_email(self): | |
self._validate_data() | |
msg = EmailMessage() | |
if self._is_html: | |
msg.add_alternative(self._body.strip(), subtype='html') | |
else: | |
msg.set_content(self._body.strip()) | |
msg['Subject'] = self._subject.strip() | |
if self._from_name and isinstance(self._from_name, str): | |
msg['From'] = Address(self._from_name.strip(), *self._from_address.split('@')) | |
else: | |
msg['From'] = Address(*self._from_address.split('@')) | |
msg['To'] = self._to_addresses | |
return msg | |
def send(self): | |
with smtplib.SMTP('localhost') as s: | |
s.send_message(self._build_email()) | |
def main(): | |
import sys, argparse | |
parser = argparse.ArgumentParser() | |
parser.add_argument('-f', '--from', dest='from_address', default=None, help="sender's email address", required=True) | |
parser.add_argument('-F', '--fullname', dest='from_name', default=os.uname()[1], help="sender's full name") | |
parser.add_argument('-t', '--to', dest='to_addresses', default=[], nargs='+', help="recipient's email addresses", required=True) | |
parser.add_argument('-s', '--subject', dest='subject', default=None, help="subject of this message", required=True) | |
parser.add_argument('-b', '--body', dest='body', default=None, help="body of this message") | |
parser.add_argument('--is-html', dest='is_html', action='store_true', help="the body is a HTML message") | |
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin) | |
parser.set_defaults(is_html=False) | |
args = parser.parse_args() | |
if len(sys.argv) == 1: | |
parser.print_help() | |
exit(1) | |
try: | |
mail = SendMail() | |
mail.subject = args.subject | |
mail.from_address = args.from_address | |
mail.from_name = args.from_name | |
mail.to_addresses = tuple(args.to_addresses) if args.to_addresses else tuple() | |
mail.is_html = args.is_html | |
mail.body = args.body if args.body else sys.stdin.read().strip() | |
mail.send() | |
except (TypeError, ValueError) as err: | |
print(err) | |
exit(1) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment