Skip to content

Instantly share code, notes, and snippets.

@daknuett
Created February 14, 2017 17:21
Show Gist options
  • Save daknuett/822b813ec20489984db3d8d2b5117fbf to your computer and use it in GitHub Desktop.
Save daknuett/822b813ec20489984db3d8d2b5117fbf to your computer and use it in GitHub Desktop.
Send emails to multiple recipients with ease
import smtplib, argparse, json, sys
from email.mime.multipart import *
from email.mime.text import *
"""
Send emails to a lot of receipients with ease.
"""
#
# Copyright(c) 2017 Daniel Knüttel
#
# This program is free software.
# Anyways if you think this program is worth it
# and we meet shout a drink for me.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Dieses Programm ist Freie Software: Sie können es unter den Bedingungen
# der GNU Affero General Public License, wie von der Free Software Foundation,
# Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren
# veröffentlichten Version, weiterverbreiten und/oder modifizieren.
#
# Dieses Programm wird in der Hoffnung, dass es nützlich sein wird, aber
# OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
# Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
# Siehe die GNU Affero General Public License für weitere Details.
#
# Sie sollten eine Kopie der GNU Affero General Public License zusammen mit diesem
# Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>.
class CollectMailDB(object):
"""
Database file:
Requires a file with the following content:
<email> <Name>
"""
def __init__(self, open_stream):
self.map = {}
for line in open_stream.read().split("\n"):
if(line == ""):
continue
address, *name = line.split()
name = " ".join(name)
self.map[address] = name
def __getitem__(self, iter_):
return self.map(iter_)
def items(self):
return self.map.items()
class CollectMailSender(object):
def __init__(self, smtp, name, db, subject, text, use_html = False, verbose = False):
self.smtp = smtp
self.name = name
self.db = db
self.use_html = use_html
self.subject = subject
self.text = text
self.verbose = verbose
def sendall(self):
if(self.verbose):
print("sending emails to {} receipients".format(len(self.db.map.items())))
for address, name in self.db.items():
if(self.verbose):
print("> sending to [{}: {}]".format(address, name))
self.do_one(address, name)
self.smtp.quit()
def do_one(self, address, name):
msg = MIMEMultipart()
msg["From"] = self.name
msg["To"] = address
msg["Subject"] = self.subject
msg.attach(MIMEText(self.text.format(myname = self.name,
name = name, address = address)))
self.smtp.sendmail(self.name, [address], msg.as_string())
help_text = \
'''
How to store my secrets?
------------------------
You have to store your secrets in a JSON file.
Then you can pass this file as an argument to
this program.
The file with you secrets must contain this information:
::
{
"uname": "<username>",
"password": "<password>",
"host": "<emailhost>"
}
How to store the receipients?
-----------------------------
The receipients (there might be a lot)
have to be stored in another file. This file
is formatted like this:
::
<email> <Name>
For instance:
::
darth-vader@death.star Lord Darth Vader
the-imperator@death.star Senator Palpatin
The name will be inserted into your email template later.
How do I send an email?
-----------------------
The easies way is to use --text for short messages, like
::
--text "Hello, {name}\n\nI would love to see you again,\nYours sincerly\n{myname}"
But you are able to send stuff from files by pipe'ing through stdin.
'''
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("secrets_file", help = "A JSON file with your login credentials")
parser.add_argument("receipients_file", help = "A file with the addresses and emails of the receipients")
parser.add_argument("-t", "--text", help = "The text to send. If no text is provided it will be read from stdin")
parser.add_argument("-s", "--subject", help = "The subject. Defaults to ''", default = "")
parser.add_argument("-o", "--oh-i-need-help", help = "Information about the mailing system", action = "store_true")
parser.add_argument("-v", "--verbose", help = "Print information during the process", action = "store_true")
parser.add_argument("-f", "--from", help = "Alternative sender")
args = parser.parse_args()
dctargs = vars(args)
if(dctargs["oh_i_need_help"]):
print(help_text)
sys.exit()
secrets = json.load(open(args.secrets_file))
uname = secrets["uname"]
sender = secrets["uname"]
password = secrets["password"]
host = secrets["host"]
port = 587
if("port" in secrets):
port = secrets["port"]
subject = args.subject
dctargs = vars(args)
text = ""
if(dctargs["text"]):
text = dctargs["text"]
else:
text = sys.stdin.read()
if("from" in dctargs and dctargs["from"]):
sender = dctargs["from"]
db = CollectMailDB(open(args.receipients_file))
smtp = smtplib.SMTP(host, port)
smtp.ehlo()
smtp.starttls()
smtp.login(uname, password)
if(args.verbose):
print("Sucessfully logged in")
sender = CollectMailSender(smtp, sender, db, subject, text, verbose = args.verbose)
sender.sendall()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment