Skip to content

Instantly share code, notes, and snippets.

@techouse
Last active February 22, 2017 13:44
Show Gist options
  • Save techouse/aabb266bc4ad3f27047b to your computer and use it in GitHub Desktop.
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/).
#!/usr/bin/env python3
__author__ = "Klemen Tušar"
__email__ = "techouse@gmail.com"
__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