Skip to content

Instantly share code, notes, and snippets.

@JustusAdam
Last active March 1, 2022 12:24
Show Gist options
  • Save JustusAdam/e97d2c9d3fcdefbb8d12 to your computer and use it in GitHub Desktop.
Save JustusAdam/e97d2c9d3fcdefbb8d12 to your computer and use it in GitHub Desktop.
Send a bunch of formatted emails using python templates and YAML/JSON data.
#!/usr/bin/env python3
"""
If you, like me, often have to send a bunch of very similer emails to a bunch of
people you may have got tired of copy pasting and resending as well.
This is my proposed solution:
This script reads a template file, using the simple python templating language
that substitutes vaules like $this with data from a dictionary.
The dictionary comes from a data file you provide. It can either be JSON or YAML
and the script will detect the type based on the file extension.
The data file has to contain the following:
- a 'credentials' key with 'host', 'user', 'password' and optionally a 'port'
for authentication with the smtp server.
- a 'values' key, containing a list of dicts/objects with keys for each substitution
one mail will be sent per item.
- a 'defaults' key containing default values which will be used should they be
absent in one of the objects from the values list
each item (values object + defaults) must contain at least a key called 'email',
containing which to send the email to.
Currenty the script just connects diretly, starts starttls and then authenticates
itself. You may change this to suite your needs by connecting wia SSH or removing
the starttls or the authentication entirely.
Usage:
./bulk_mail.py template datafile.yml
"""
from string import Template
import sys
import collections
import pathlib
import smtplib
from email.mime.text import MIMEText
def from_yaml(file):
import yaml
return yaml.load(file)
def from_json(file):
import json
return json.load(file)
loader = {
'yaml': from_yaml,
'yml': from_yaml,
'json': from_json
}
def format_mails(letter_template_raw, data):
has_def = 'defaults' in data and 'values' in data
if has_def:
defaults = data['defaults']
values = data['values']
else:
values = data
letter_template = Template(letter_template_raw)
def format_one(instance):
if has_def:
instance = collections.ChainMap(instance, defaults)
msg = MIMEText(letter_template.substitute(instance))
msg['To'] = instance['email']
msg['Subject'] = instance['subject']
msg['From'] = instance['from']
if 'cc' in instance:
msg['CC'] = instance['cc']
return instance['email'], msg
return map(format_one, values)
def main():
_, letter_file, data_file = sys.argv
with open(letter_file, 'r') as f:
letter = f.read()
with open(data_file) as f:
_, ext = data_file.rsplit('.', 1)
data = loader.get(ext, from_yaml)(f)
credentials = data['credentials']
with smtplib.SMTP(credentials['host'], credentials.get('port', 0)) as smtp:
smtp.starttls()
smtp.login(credentials['user'], credentials['password'])
for address, msg in format_mails(letter, data):
smtp.send_message(msg)
print('Sent message to: ', address)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment