Отправка большого количества писем с помощью amazon ses и python - PullRequest
0 голосов
/ 09 апреля 2020

Я отправляю большую часть писем с вложением html, используя amazon ses и python. Я сохранил все идентификаторы электронной почты в таблице в базе данных postgres. Наряду с этим я также включаю ссылку для отмены подписки, чтобы в случае, если любой пользователь щелкнет ссылку для отмены подписки, идентификатор электронной почты этого пользователя будет сохранен в моей локальной таблице unsubscribe_list. Также я проверяю, что если в списке отписавшихся нет идентификатора электронной почты, на который я отправляю письмо. Все это работает отлично. Но для отправки писем требуется много времени. Это потому, что я устанавливаю связь каждый раз. Так что, если у кого-нибудь есть какое-либо решение, как настроить амазонное соединение один раз, а затем отправить все электронные письма, которые есть в моей базе данных, а затем закрыть соединение. Если у кого-нибудь есть предложения, пожалуйста, дайте мне знать. Заранее спасибо.

Это мой код

import smtplib
import email.utils
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, Header

from lib import db, file
from lib.config import CREDS



def send_email_amazon_ses_smtp(
    from_email, from_name, to_email, to_name, subject, html_content,
    plain_text_content, req_token, enc_email
):
    # Create message container - the correct MIME type is multipart/alternative.
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = email.utils.formataddr((from_name, from_email))
    msg['To'] = email.utils.formataddr((from_name, from_email))
    # Comment or delete the next line if you are not using a configuration set
    msg.add_header('List-ID', f"<{CREDS['list_id']}>")
    msg.add_header(
        'List-Unsubscribe',
        f"http://{CREDS['unsubscriber_domain']}/?p1={enc_email}&p2={req_token}"
    )

    # Record the MIME types of both parts - text/plain and text/html.
    plain_text_part = MIMEText(plain_text_content if plain_text_content else "", 'plain')
    html_part = MIMEText(html_content, 'html')
    # Attach parts into message container.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    if plain_text_content: msg.attach(plain_text_part)
    msg.attach(html_part)

    # Try to send the message.
    try:
        server = smtplib.SMTP(CREDS['amazon_ses_host'], CREDS['amazon_ses_port'])
        server.ehlo()
        server.starttls()
        #stmplib docs recommend calling ehlo() before & after starttls()
        server.ehlo()
        server.login(CREDS['amazon_ses_username'], CREDS['amazon_ses_password'])
        server.sendmail(from_email, to_email, msg.as_string())
        server.close()
    # Display an error message if something goes wrong.
    except Exception as e:
        print ("Error: ", e)
    else:
        print ("Email sent with Amazon!")


def email_in_unsub_list(email):
    "Check if email is in unsubscribe_list table"

    c, conn = db.connection()

    query = "SELECT id FROM unsubscribe_list WHERE email=%s"
    c.execute(query, (email, ))
    email = c.fetchone()

    c.close()
    conn.close()

    if email: return True
    return False


def form_lead_dict(lead):
    leads = {
        "id": lead[0],
        "first_name": lead[1] if lead[1].capitalize() else None,
        "last_name": lead[2] if lead[1].capitalize() else None,
        "full_name": lead[3] if lead[1].capitalize() else None,
        "email": lead[4],
        "templates_sent": lead[5],
        "lead_types": lead[6],
    }
    return leads


def send_messages(template_filename, subject, from_email, from_name, lead_type, provider, how_many):
    leads = get_suited_leads(template_filename, lead_type, how_many)
    lead_list = []
    for lead in leads:
        lead = form_lead_dict(lead)
        lead_list.append(lead)

    send_to_list(template_filename, subject, from_email, from_name, lead_list, provider)


def update_lead_record(lead_id, template_filename):
    c, conn = db.connection()

    query = """
        UPDATE es_lead
        SET templates_sent = array_append(templates_sent, %s)
        WHERE id=%s
    """
    values = (template_filename, lead_id)
    c.execute(query, values)
    conn.commit()

    c.close()
    conn.close()


def send_to_list(template_filename, subject, from_email, from_name, lead_list, provider):
    "Send emails to lead_list"
    for lead in lead_list:
        print(lead)
        from_email = from_email
        from_name = from_name
        to_email = lead["email"]
        to_name = lead["first_name"]
        subject = subject
        # prepare html and plain text content if .txt version of file exists
        html_content, plain_text_content, req_token, enc_email = \
            file.prepare_template(
                template_filename,
                to_email,
                to_name,
                CREDS['unsubscriber_domain']
            )

        email_sending_function[provider](
            from_email, from_name, to_email, to_name, subject, html_content, plain_text_content,
            req_token, enc_email
        )
        update_lead_record(lead["id"], template_filename)


def get_suited_leads(template_filename, lead_type, how_many):
    """
        Return list of lead dicts that meet the conditions and are not present
        in unsubscribe_list table.
    """

    c, conn = db.connection()

    query = """
SELECT id, first_name, last_name, full_name, email, templates_sent, lead_types
FROM es_lead
WHERE (NOT %s = ANY(templates_sent) OR templates_sent IS NULL)
AND %s = ANY(lead_types)
AND NOT EXISTS (SELECT 1 FROM unsubscribe_list WHERE unsubscribe_list.email = es_lead.email)
ORDER BY id
LIMIT %s
    """
    values = (template_filename, lead_type, how_many)
    c.execute(query, values)

    leads = c.fetchall()

    c.close()
    conn.close()

    return leads


email_sending_function = {
    "Amazon": send_email_amazon_ses_smtp
}



...