Skip to content

Instantly share code, notes, and snippets.

@jgsogo
Last active October 2, 2017 12:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jgsogo/e3f7faf6ad19721d4c7b6393f65f7fec to your computer and use it in GitHub Desktop.
Save jgsogo/e3f7faf6ad19721d4c7b6393f65f7fec to your computer and use it in GitHub Desktop.
Send mass mail using GMail
import os
import sys
import logging
from send_mail import gmail_send
log = logging.getLogger(__name__)
_LOG_LEVEL_STRINGS = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']
def read_data(filename, headers):
def dequote(s):
if (s[0] == s[-1]) and s.startswith(("'", '"')):
return s[1:-1]
return s
ret = []
with open(filename, 'r') as f:
for line in f.readlines():
if line.startswith("#") or not len(line.strip()): continue
data = [dequote(item.strip()) for item in line.split(',')]
if len(data) != len(headers):
log.warning("Line discarded. Fields do not match with headers: {!r}".format(line))
ret.append({key: value for key, value in zip(headers, data)})
return ret
def generate_pdf(params, output_filename):
# TODO: implement a more flexible way to doy it
log.debug(" - Generate PDF to {}".format(output_filename))
return output_filename
def send_mail(address, attachment):
log.debug(" - Send email to {}".format(address))
subject = "Prueba batch" # TODO: Whatever...
text = "Contenido del mensaje" # TODO: More blablabla
gmail_send([address], subject, text, attachments=[attachment])
return False
def _log_level_string_to_int(log_level_string):
if not log_level_string in _LOG_LEVEL_STRINGS:
message = 'invalid choice: {0} (choose from {1})'.format(log_level_string, _LOG_LEVEL_STRINGS)
raise argparse.ArgumentTypeError(message)
log_level_int = getattr(logging, log_level_string, logging.INFO)
# check the logging log_level_choices have not changed from our expected values
assert isinstance(log_level_int, int)
return log_level_int
if __name__ == '__main__':
import argparse
GENERATE = ['generate-pdf', 'generate-and-send']
SEND = ['send-pdf', 'generate-and-send']
ACTIONS = set(GENERATE + SEND)
me_path = os.path.dirname(__file__)
tmp_path_default = os.path.abspath(os.path.join(me_path, 'tmp'))
parser = argparse.ArgumentParser(description='So we are to spam ^^')
parser.add_argument('--action', choices=ACTIONS, required=True)
parser.add_argument('--data-file', dest='data_file', required=True)
parser.add_argument('--log-level', default='INFO', dest='log_level',
type=_log_level_string_to_int, nargs='?',
help='Set the logging output level. {0}'.format(_LOG_LEVEL_STRINGS))
parser.add_argument('--tmp-dir', dest='tmp_dir', default=tmp_path_default)
args = parser.parse_args()
# Configure log
ch = logging.StreamHandler()
ch.setLevel(args.log_level)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)7s - %(message)s')
ch.setFormatter(formatter)
root_logger = logging.getLogger()
root_logger.addHandler(ch)
root_logger.setLevel(args.log_level)
sys.stdout.write("="*20)
sys.stdout.write("\nSEND MASS MAIL WITH ATTACHMENT\n")
sys.stdout.write("\n + data_file: {}".format(args.data_file))
sys.stdout.write("\n + action: {}".format(args.action))
sys.stdout.write("\n + tmp directory: {}".format(args.tmp_dir))
sys.stdout.write("\n")
if not os.path.exists(args.tmp_dir):
os.makedirs(args.tmp_dir)
data = read_data(args.data_file, headers=["address", "name"])
total = len(data)
i = 1
sys.stdout.write("\nWork over {} lines".format(total))
for it in data:
sys.stdout.write("\n[{}/{}] {}".format(i, total, it['name']))
sys.stdout.flush()
valid_filename = "".join(x for x in it['address'] if x.isalnum())
valid_filename = valid_filename + '.pdf'
pdf_filename = os.path.join(args.tmp_dir, valid_filename)
if args.action in GENERATE:
sys.stdout.write(" > generating")
sys.stdout.flush()
generate_pdf(it, pdf_filename)
if args.action in SEND:
sys.stdout.write(" > sending")
sys.stdout.flush()
send_mail(it['address'], pdf_filename)
i = i+1
sys.stdout.write("\n Done! \n")
import sys
import os
import smtplib
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import logging
log = logging.getLogger(__name__)
try:
SENDER = os.environ['MASS_MAIL_SENDER']
PASSWORD = os.environ['MASS_MAIL_PASSWORD']
except KeyError as e:
sys.stderr.write("Add environment variables 'MASS_MAIL_SENDER' and 'MASS_MAIL_PASSWORD'."
"Use something like the following in your console:"
"$export MASS_MAIL_SENDER=mymail@gmail.com\n")
exit()
def gmail_send(to, subject, text, attachments=[]):
assert type(to)==list
assert type(attachments)==list
# Create the enclosing (outer) message
outer = MIMEMultipart()
outer['Subject'] = subject
outer['To'] = ', '.join(to)
outer['From'] = SENDER
outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
outer.attach(MIMEText(text, 'plain')) # or 'html'
# Add the attachments to the message
for file in attachments:
try:
with open(file, 'rb') as fp:
msg = MIMEBase('application', "octet-stream")
msg.set_payload(fp.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(file))
outer.attach(msg)
except:
print("Unable to open one of the attachments. Error: ", sys.exc_info()[0])
raise
composed = outer.as_string()
# Send the email
try:
with smtplib.SMTP_SSL('smtp.googlemail.com', 465) as s:
s.login(SENDER, PASSWORD)
s.sendmail(SENDER, to, composed)
s.close()
log.debug("Email sent!")
except smtplib.SMTPAuthenticationError as e:
sys.stderr.write("\n\nERROR! Your password is wrong or you need to allow 'less secure apps' in "
"your GMail account (+info. https://support.google.com/accounts/answer/6010255?hl=en)\n\n")
exit()
except:
print("Unable to send the email. Error: ", sys.exc_info()[0])
raise
@Dputz
Copy link

Dputz commented Oct 2, 2017

To run this would we need both codes in the google sheet?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment