apb_extra_utils.send_mail
1# coding=utf-8 2# 3# Author: Ernesto Arredondo Martinez (ernestone@gmail.com) 4# Created: 7/6/19 18:23 5# Last modified: 7/6/19 18:21 6# Copyright (c) 2019 7 8# Functions to send mail from a server (environment variable MAIL_SERVER) 9 10import datetime 11import mimetypes 12import os 13import smtplib 14import ssl 15import warnings 16 17import docutils.core 18from sendgrid import SendGridAPIClient 19from sendgrid.helpers.mail import Mail 20 21from .misc import machine_apb, machine_name 22 23 24def set_attachment_to_msg(msg, file_path): 25 """ 26 27 Args: 28 msg (EmailMessage): objecto EmailMessage donde se hara attach 29 file_path: path del fichero a vincular al mensaje 30 31 Returns: 32 33 """ 34 if not os.path.isfile(file_path): 35 return 36 37 # Guess the content type based on the file's extension. Encoding 38 # will be ignored, although we should check for simple things like 39 # gzip'd or compressed files. 40 ctype, encoding = mimetypes.guess_type(file_path) 41 if ctype is None or encoding is not None: 42 # No guess could be made, or the file is encoded (compressed), so 43 # use a generic bag-of-bits type. 44 ctype = 'application/octet-stream' 45 maintype, subtype = ctype.split('/', 1) 46 with open(file_path, 'rb') as fp: 47 msg.add_attachment(fp.read(), 48 maintype=maintype, 49 subtype=subtype, 50 filename=os.path.basename(file_path)) 51 52 53def sendMailWithAttach(server=os.environ.get('MAIL_SERVER', 'server-mail.com'), frm='', to='', subject='', body='', 54 lineSep='not_line_separator', files=None, to_html=False, tls=True): 55 """ 56 Permet enviar un E-mail des de FROM a TO amb SUBJECT amb BOBY line_separator (cas body amb multilinea) i ATTACH 57 58 Args: 59 server (str=os.environ.get('MAIL_SERVER'): 60 frm (str=""): 61 to (str=""): 62 subject (str=""): 63 body (str=""): 64 lineSep (str="not_line_separator"): 65 files (list=None): lista de paths de ficheros a adjuntar 66 to_html (bool=False): Si true parsea con docutils (reestructuredText [rst], Latex, ...) 67 el texto del body enviado y lo convierte a html 68 tls (bool=True): start TLS 69 Returns: 70 71 """ 72 from email.message import EmailMessage 73 74 msg = EmailMessage() 75 76 msg['From'] = frm 77 msg['To'] = to 78 msg['Subject'] = subject 79 msg.epilogue = '' 80 81 if lineSep != 'not_line_separator' and body.find(lineSep) >= 0: 82 body = '\n'.join(body.split(lineSep)) 83 84 msg.set_content(body) 85 if to_html: 86 msg.add_alternative(docutils.core.publish_string(body, writer_name="html").decode('utf-8'), subtype='html') 87 88 if files: 89 for file_path in files: 90 set_attachment_to_msg(msg, file_path) 91 92 context = None 93 if tls: 94 context = ssl.create_default_context() 95 srv = None 96 try: 97 codi = 0 98 srv = smtplib.SMTP(server) 99 srv.ehlo() 100 if tls: 101 try: 102 srv.starttls(context=context) 103 except smtplib.SMTPNotSupportedError as exc: 104 print(f"El server SMTP '{server}' no suporta TLS. Error: {exc}") 105 srv.ehlo() 106 srv.send_message(msg) 107 except smtplib.SMTPException as exc: 108 import traceback 109 print(traceback.format_exc()) 110 codi = 1 111 finally: 112 if srv: 113 srv.quit() 114 115 return codi 116 117 118FROM_MAIL = os.getenv('DEFAULT_FROM_MAIL', 'from_your_account@mail.com') 119 120 121def enviar_mail(subject, body, user_mail_list, to_html=False, *attach_path_files): 122 """ 123 Envia mail desde la cuenta FROM_MAIL a la lista de mails especificados y adjunta los logs del gestor 124 si estos han generado entradas 125 126 Args: 127 subject (str): Le asignará el nombre de la máquina desde la que está corriendo 128 body (str): Texto con el cuerpo del mail. Por defecto buscará '$$NEWLINE$$' para substituir por saltos de línea 129 user_mail_list (list): Lista de strings con los correos 130 to_html (bool=False): Si true parsea con docutils (reestructuredText [rst], Latex, ...) 131 el texto del body enviado y lo convierte a html 132 *attach_path_files: PATHs de ficheros a adjuntar 133 134 Returns: 135 codi (int) 136 """ 137 codi = 1 138 if machine_apb(): 139 subject = f"[{machine_name()}] {subject}" 140 141 # SendMail 142 try: 143 codi = sendMailWithAttach(frm=FROM_MAIL, 144 to=", ".join(user_mail_list), 145 subject="{} {}".format(subject, 146 datetime.datetime.now().strftime( 147 '%Y-%m-%d %H:%M')), 148 body=body, 149 lineSep='$$NEWLINE$$', 150 files=list(attach_path_files), 151 to_html=to_html) 152 153 except Exception as exc: 154 import traceback 155 print(traceback.format_exc()) 156 warnings.warn("No se ha podido enviar el mail con subject '{subject}'".format(subject=subject)) 157 158 return codi 159 160 161def send_grid(subject: str, body: str, user_mail_list: list, sender: str = None, api_key: str = None): 162 """ 163 Envia mail desde la api de sendGrid 164 165 Args: 166 subject (str): Tema a enviar en el correo 167 body (str): Texto con el cuerpo del mail. Por defecto buscará '$$NEWLINE$$' para substituir por saltos de línea 168 user_mail_list (list): Lista de strings con los correos 169 sender (str=None): Mail del sender. Si no el passen, agafem variable d'entorn SENDGRID_SENDER 170 api_key(str=None): Api key del send grid a utilitzar. Si no el passen, agafem variable d'entorn SENDGRID_API_KEY 171 172 Returns: 173 dict: Diccionario con la respuesta de la api de sendGrid 174 Examples: 175 OK = {'status_code': 202, 'body': ...} 176 177 """ 178 if not api_key: 179 api_key = os.getenv('SENDGRID_API_KEY') 180 181 if not sender: 182 sender = os.getenv('SENDGRID_SENDER') 183 184 resp = dict() 185 try: 186 message = Mail( 187 from_email=sender, 188 to_emails=user_mail_list, 189 subject=subject, 190 html_content=body) 191 192 sg = SendGridAPIClient(api_key) 193 response = sg.send(message) 194 195 resp['status_code'] = response.status_code 196 resp['body'] = response.body 197 198 except Exception as exc: 199 error = f"No se ha podido enviar el mail con subject '{subject}'\n" \ 200 f"Error: {exc}" 201 resp['error'] = error 202 warnings.warn(error) 203 204 return resp 205 206 207if __name__ == '__main__': 208 import fire 209 210 fire.Fire({ 211 enviar_mail.__name__: enviar_mail, 212 send_grid.__name__: send_grid 213 })
def
set_attachment_to_msg(msg, file_path):
25def set_attachment_to_msg(msg, file_path): 26 """ 27 28 Args: 29 msg (EmailMessage): objecto EmailMessage donde se hara attach 30 file_path: path del fichero a vincular al mensaje 31 32 Returns: 33 34 """ 35 if not os.path.isfile(file_path): 36 return 37 38 # Guess the content type based on the file's extension. Encoding 39 # will be ignored, although we should check for simple things like 40 # gzip'd or compressed files. 41 ctype, encoding = mimetypes.guess_type(file_path) 42 if ctype is None or encoding is not None: 43 # No guess could be made, or the file is encoded (compressed), so 44 # use a generic bag-of-bits type. 45 ctype = 'application/octet-stream' 46 maintype, subtype = ctype.split('/', 1) 47 with open(file_path, 'rb') as fp: 48 msg.add_attachment(fp.read(), 49 maintype=maintype, 50 subtype=subtype, 51 filename=os.path.basename(file_path))
Arguments:
- msg (EmailMessage): objecto EmailMessage donde se hara attach
- file_path: path del fichero a vincular al mensaje
Returns:
def
sendMailWithAttach( server='server-mail.com', frm='', to='', subject='', body='', lineSep='not_line_separator', files=None, to_html=False, tls=True):
54def sendMailWithAttach(server=os.environ.get('MAIL_SERVER', 'server-mail.com'), frm='', to='', subject='', body='', 55 lineSep='not_line_separator', files=None, to_html=False, tls=True): 56 """ 57 Permet enviar un E-mail des de FROM a TO amb SUBJECT amb BOBY line_separator (cas body amb multilinea) i ATTACH 58 59 Args: 60 server (str=os.environ.get('MAIL_SERVER'): 61 frm (str=""): 62 to (str=""): 63 subject (str=""): 64 body (str=""): 65 lineSep (str="not_line_separator"): 66 files (list=None): lista de paths de ficheros a adjuntar 67 to_html (bool=False): Si true parsea con docutils (reestructuredText [rst], Latex, ...) 68 el texto del body enviado y lo convierte a html 69 tls (bool=True): start TLS 70 Returns: 71 72 """ 73 from email.message import EmailMessage 74 75 msg = EmailMessage() 76 77 msg['From'] = frm 78 msg['To'] = to 79 msg['Subject'] = subject 80 msg.epilogue = '' 81 82 if lineSep != 'not_line_separator' and body.find(lineSep) >= 0: 83 body = '\n'.join(body.split(lineSep)) 84 85 msg.set_content(body) 86 if to_html: 87 msg.add_alternative(docutils.core.publish_string(body, writer_name="html").decode('utf-8'), subtype='html') 88 89 if files: 90 for file_path in files: 91 set_attachment_to_msg(msg, file_path) 92 93 context = None 94 if tls: 95 context = ssl.create_default_context() 96 srv = None 97 try: 98 codi = 0 99 srv = smtplib.SMTP(server) 100 srv.ehlo() 101 if tls: 102 try: 103 srv.starttls(context=context) 104 except smtplib.SMTPNotSupportedError as exc: 105 print(f"El server SMTP '{server}' no suporta TLS. Error: {exc}") 106 srv.ehlo() 107 srv.send_message(msg) 108 except smtplib.SMTPException as exc: 109 import traceback 110 print(traceback.format_exc()) 111 codi = 1 112 finally: 113 if srv: 114 srv.quit() 115 116 return codi
Permet enviar un E-mail des de FROM a TO amb SUBJECT amb BOBY line_separator (cas body amb multilinea) i ATTACH
Arguments:
- server (str=os.environ.get('MAIL_SERVER'):
- frm (str=""):
- to (str=""):
- subject (str=""):
- body (str=""):
- lineSep (str="not_line_separator"):
- files (list=None): lista de paths de ficheros a adjuntar
- to_html (bool=False): Si true parsea con docutils (reestructuredText [rst], Latex, ...) el texto del body enviado y lo convierte a html
- tls (bool=True): start TLS
Returns:
FROM_MAIL =
'from_your_account@mail.com'
def
enviar_mail(subject, body, user_mail_list, to_html=False, *attach_path_files):
122def enviar_mail(subject, body, user_mail_list, to_html=False, *attach_path_files): 123 """ 124 Envia mail desde la cuenta FROM_MAIL a la lista de mails especificados y adjunta los logs del gestor 125 si estos han generado entradas 126 127 Args: 128 subject (str): Le asignará el nombre de la máquina desde la que está corriendo 129 body (str): Texto con el cuerpo del mail. Por defecto buscará '$$NEWLINE$$' para substituir por saltos de línea 130 user_mail_list (list): Lista de strings con los correos 131 to_html (bool=False): Si true parsea con docutils (reestructuredText [rst], Latex, ...) 132 el texto del body enviado y lo convierte a html 133 *attach_path_files: PATHs de ficheros a adjuntar 134 135 Returns: 136 codi (int) 137 """ 138 codi = 1 139 if machine_apb(): 140 subject = f"[{machine_name()}] {subject}" 141 142 # SendMail 143 try: 144 codi = sendMailWithAttach(frm=FROM_MAIL, 145 to=", ".join(user_mail_list), 146 subject="{} {}".format(subject, 147 datetime.datetime.now().strftime( 148 '%Y-%m-%d %H:%M')), 149 body=body, 150 lineSep='$$NEWLINE$$', 151 files=list(attach_path_files), 152 to_html=to_html) 153 154 except Exception as exc: 155 import traceback 156 print(traceback.format_exc()) 157 warnings.warn("No se ha podido enviar el mail con subject '{subject}'".format(subject=subject)) 158 159 return codi
Envia mail desde la cuenta FROM_MAIL a la lista de mails especificados y adjunta los logs del gestor si estos han generado entradas
Arguments:
- subject (str): Le asignará el nombre de la máquina desde la que está corriendo
- body (str): Texto con el cuerpo del mail. Por defecto buscará '$$NEWLINE$$' para substituir por saltos de línea
- user_mail_list (list): Lista de strings con los correos
- to_html (bool=False): Si true parsea con docutils (reestructuredText [rst], Latex, ...) el texto del body enviado y lo convierte a html
- *attach_path_files: PATHs de ficheros a adjuntar
Returns:
codi (int)
def
send_grid( subject: str, body: str, user_mail_list: list, sender: str = None, api_key: str = None):
162def send_grid(subject: str, body: str, user_mail_list: list, sender: str = None, api_key: str = None): 163 """ 164 Envia mail desde la api de sendGrid 165 166 Args: 167 subject (str): Tema a enviar en el correo 168 body (str): Texto con el cuerpo del mail. Por defecto buscará '$$NEWLINE$$' para substituir por saltos de línea 169 user_mail_list (list): Lista de strings con los correos 170 sender (str=None): Mail del sender. Si no el passen, agafem variable d'entorn SENDGRID_SENDER 171 api_key(str=None): Api key del send grid a utilitzar. Si no el passen, agafem variable d'entorn SENDGRID_API_KEY 172 173 Returns: 174 dict: Diccionario con la respuesta de la api de sendGrid 175 Examples: 176 OK = {'status_code': 202, 'body': ...} 177 178 """ 179 if not api_key: 180 api_key = os.getenv('SENDGRID_API_KEY') 181 182 if not sender: 183 sender = os.getenv('SENDGRID_SENDER') 184 185 resp = dict() 186 try: 187 message = Mail( 188 from_email=sender, 189 to_emails=user_mail_list, 190 subject=subject, 191 html_content=body) 192 193 sg = SendGridAPIClient(api_key) 194 response = sg.send(message) 195 196 resp['status_code'] = response.status_code 197 resp['body'] = response.body 198 199 except Exception as exc: 200 error = f"No se ha podido enviar el mail con subject '{subject}'\n" \ 201 f"Error: {exc}" 202 resp['error'] = error 203 warnings.warn(error) 204 205 return resp
Envia mail desde la api de sendGrid
Arguments:
- subject (str): Tema a enviar en el correo
- body (str): Texto con el cuerpo del mail. Por defecto buscará '$$NEWLINE$$' para substituir por saltos de línea
- user_mail_list (list): Lista de strings con los correos
- sender (str=None): Mail del sender. Si no el passen, agafem variable d'entorn SENDGRID_SENDER
- api_key(str=None): Api key del send grid a utilitzar. Si no el passen, agafem variable d'entorn SENDGRID_API_KEY
Returns:
dict: Diccionario con la respuesta de la api de sendGrid Examples: OK = {'status_code': 202, 'body': ...}