yagmail.sender 源代码

# when there is a bcc a different message has to be sent to the bcc
# person, to show that they are bcc'ed

import time
import logging
import smtplib

from yagmail.log import get_logger
from yagmail.utils import find_user_home_path
from yagmail.oauth2 import get_oauth2_info, get_oauth_string
from yagmail.headers import resolve_addresses
from yagmail.validate import validate_email_with_regex
from yagmail.password import handle_password
from yagmail.message import prepare_message
from yagmail.headers import make_addr_alias_user


[文档]class SMTP: """ :class:`yagmail.SMTP` is a magic wrapper around ``smtplib``'s SMTP connection, and allows messages to be sent.""" def __init__( self, user=None, password=None, host="smtp.gmail.com", port=None, smtp_starttls=None, smtp_ssl=True, smtp_set_debuglevel=0, smtp_skip_login=False, encoding="utf-8", oauth2_file=None, soft_email_validation=True, dkim=None, **kwargs ): self.log = get_logger() self.set_logging() self.soft_email_validation = soft_email_validation if oauth2_file is not None: oauth2_info = get_oauth2_info(oauth2_file, user) if user is None: user = oauth2_info["email_address"] if smtp_skip_login and user is None: user = "" elif user is None: user = find_user_home_path() self.user, self.useralias = make_addr_alias_user(user) if soft_email_validation: validate_email_with_regex(self.user) self.is_closed = None self.host = host self.port = str(port) if port is not None else "465" if smtp_ssl else "587" self.smtp_starttls = smtp_starttls self.ssl = smtp_ssl self.smtp_skip_login = smtp_skip_login self.debuglevel = smtp_set_debuglevel self.encoding = encoding self.kwargs = kwargs self.cache = {} self.unsent = [] self.num_mail_sent = 0 self.oauth2_file = oauth2_file self.credentials = password if oauth2_file is None else oauth2_info self.dkim = dkim def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if not self.is_closed: self.close() return False @property def connection(self): return smtplib.SMTP_SSL if self.ssl else smtplib.SMTP @property def starttls(self): if self.smtp_starttls is None: return False if self.ssl else True return self.smtp_starttls
[文档] def set_logging(self, log_level=logging.ERROR, file_path_name=None): """ This function allows to change the logging backend, either output or file as backend It also allows to set the logging level (whether to display only critical/error/info/debug. for example:: yag = yagmail.SMTP() yag.set_logging(yagmail.logging.DEBUG) # to see everything and:: yagmail.set_logging(yagmail.logging.DEBUG, 'somelocalfile.log') lastly, a log_level of :py:class:`None` will make sure there is no I/O. """ self.log = get_logger(log_level, file_path_name)
def prepare_send( self, to=None, subject=None, contents=None, attachments=None, cc=None, bcc=None, headers=None, prettify_html=True, message_id=None, group_messages=True, ): addresses = resolve_addresses(self.user, self.useralias, to, cc, bcc) if self.soft_email_validation: for email_addr in addresses["recipients"]: validate_email_with_regex(email_addr) msg = prepare_message( self.user, self.useralias, addresses, subject, contents, attachments, headers, self.encoding, prettify_html, message_id, group_messages, self.dkim, ) recipients = addresses["recipients"] msg_strings = msg.as_string() return recipients, msg_strings
[文档] def send( self, to=None, subject=None, contents=None, attachments=None, cc=None, bcc=None, preview_only=False, headers=None, prettify_html=True, message_id=None, group_messages=True, ): """ Use this to send an email with gmail""" self.login() recipients, msg_strings = self.prepare_send( to, subject, contents, attachments, cc, bcc, headers, prettify_html, message_id, group_messages, ) if preview_only: return recipients, msg_strings return self._attempt_send(recipients, msg_strings)
def _attempt_send(self, recipients, msg_strings): attempts = 0 while attempts < 3: try: result = self.smtp.sendmail(self.user, recipients, msg_strings) self.log.info("Message sent to %s", recipients) self.num_mail_sent += 1 return result except smtplib.SMTPServerDisconnected as e: self.log.error(e) attempts += 1 time.sleep(attempts * 3) self.unsent.append((recipients, msg_strings)) return False
[文档] def send_unsent(self): """ Emails that were not being able to send will be stored in :attr:`self.unsent`. Use this function to attempt to send these again """ for i in range(len(self.unsent)): recipients, msg_strings = self.unsent.pop(i) self._attempt_send(recipients, msg_strings)
[文档] def close(self): """ Close the connection to the SMTP server """ self.is_closed = True try: self.smtp.quit() except (TypeError, AttributeError, smtplib.SMTPServerDisconnected): pass
def login(self): if self.oauth2_file is not None: self._login_oauth2(self.credentials) else: self._login(self.credentials) def _login(self, password): """ Login to the SMTP server using password. `login` only needs to be manually run when the connection to the SMTP server was closed by the user. """ self.smtp = self.connection(self.host, self.port, **self.kwargs) self.smtp.set_debuglevel(self.debuglevel) if self.starttls: self.smtp.ehlo() if self.starttls is True: self.smtp.starttls() else: self.smtp.starttls(**self.starttls) self.smtp.ehlo() self.is_closed = False if not self.smtp_skip_login: password = self.handle_password(self.user, password) self.smtp.login(self.user, password) self.log.info("Connected to SMTP @ %s:%s as %s", self.host, self.port, self.user) @staticmethod def handle_password(user, password): return handle_password(user, password) @staticmethod def get_oauth_string(user, oauth2_info): return get_oauth_string(user, oauth2_info) def _login_oauth2(self, oauth2_info): if "email_address" in oauth2_info: oauth2_info.pop("email_address") self.smtp = self.connection(self.host, self.port, **self.kwargs) try: self.smtp.set_debuglevel(self.debuglevel) except AttributeError: pass auth_string = self.get_oauth_string(self.user, oauth2_info) self.smtp.ehlo(oauth2_info["google_client_id"]) if self.starttls is True: self.smtp.starttls() self.smtp.docmd("AUTH", "XOAUTH2 " + auth_string)
[文档] def feedback(self, message="Awesome features! You made my day! How can I contribute?"): """ Most important function. Please send me feedback :-) """ self.send("kootenpv@gmail.com", "Yagmail feedback", message)
def __del__(self): try: if not self.is_closed: self.close() except AttributeError: pass