Murat Demir
Murat Demir

Reputation: 716

Can I send different mails to different receivers with different messages

I'm trying to send emails with different messages to different receivers via libsmtp and gmail in a loop.

I tried to send mail without closing smtp connection. It just send all message to same receiver but I would like to send messages with receivers. I tried to open and quit connection for each loop but after first iteration executes and quits the connection, second iteration gives error message "smtplib.SMTPServerDisconnected: please run connect() first" even though it should have connect again.

class Mail:
    msg = MIMEMultipart()
    def __init__(self, addr_from, addr_to):
        self.msg['From'] = addr_from
        self.msg['To'] = addr_to
    def set_subject(self, subject):
        self.msg['Subject'] = subject
    def set_message(self, message):
        self.msg.attach(MIMEText(message, 'plain'))
    def attach_image(self, image_location, image_name):
        fp = open(image_location, 'rb')
        img = MIMEImage(fp.read())
        img.add_header('Content-Disposition', "attachment; filename= %s" % image_name)
        fp.close()
        self.msg.attach(img)
    def get_mail(self):
        return self.msg



class EmailServer:
    server = smtplib.SMTP('smtp.gmail.com', 587)
    def __init__(self, sender_mail, password):
        self.sender_mail = sender_mail
        self.password = password
        self.server.ehlo()
        self.server.starttls()
        self.server.login(sender_mail, password)

    def send_mail(self, mail):
        self.server.sendmail(mail['From'], mail['To'], mail.as_string())

    def quit_server(self):
        self.server.quit()

for row in range(0,x):

    name = sheet.cell_value(row, 0)
    mail_adress = sheet.cell_value(row, 1)

    print(name, mail_adress)

    mail = Mail('[email protected]', mail_adress)
    mail.set_subject("subject")
    mail.set_message('message')
    server = EmailServer('[email protected]', 'xxxxxxxxx')
    mail.attach_image('/Users/xxxx/xxxx/xxxx.jpg', 'title')
    server.send_mail(mail.get_mail())
    server.quit_server()

Upvotes: 1

Views: 219

Answers (1)

wwii
wwii

Reputation: 23743

The way you defined Mail, msg is a class attribute so each instance of Mail contains the same msg.

import email
from email.mime.multipart import MIMEMultipart

class Mail:
    msg = MIMEMultipart()
    def __init__(self, addr_from, addr_to):
        self.msg['From'] = addr_from
        self.msg['To'] = addr_to

for afrom, ato in zip(list('abcd'),list('wert')):
    mail = Mail(afrom, ato)
    print(id(mail.msg))

In [60]: 
160675264
160675264
160675264
160675264

MIMEMultipart is an email.message object; its __setitem__ docstring states: ...does not overwrite an existing header with the same field name. Use __delitem__() first to delete any existing headers. ...

In [61]: help(mail.msg.__setitem__)
Help on method __setitem__ in module email.message:

__setitem__(name, val) method of email.mime.multipart.MIMEMultipart instance
    Set the value of a header.

    Note: this does not overwrite an existing header with the same field
    name.  Use __delitem__() first to delete any existing headers.

After the for loop, the From and To headers are lists with multiple values:

mail.msg.get_all('From'), mail.msg.get_all('To')
Out[62]: (['a', 'b', 'c', 'd'], ['w', 'e', 'r', 't'])

One solution to your problem would be to delete the From and To headers before assigning a new value in the loop.

In [63]: del mail.msg['From']

In [64]: del mail.msg['To']

In [65]: mail.msg.get_all('From'), mail.msg.get_all('To')
Out[65]: (None, None)

In [66]: mail.msg['From'] = 'z'

In [67]: mail.msg['To'] = 'y'

In [68]: mail.msg.get_all('From'), mail.msg.get_all('To')
Out[68]: (['z'], ['y'])

Another option might be to create a new msg for each Mail instance.

class Mail:
    def __init__(self, addr_from, addr_to):
        self.msg = MIMEMultipart()
        self.msg['From'] = addr_from
        self.msg['To'] = addr_to

Another option would be to send the mail AFTER the loop. SMTP.sendmail's to_addrs parameter accepts a list so EmailServer.sendmail needs to be modified

class EmailServer:
    ...
    ...

    def send_mail(self, mail):
        self.server.sendmail(mail['From'], mail.get_all('To'), mail.as_string())

Then After a loop that adds multiple 'To' addresses

server.send_mail(mail.get_mail())
server.quit_server()

I'm not able to test this one but it should work. You should probably create just one Mail instance with all the stuff that doesn't change; and one EmailServer instance then use the loop to add multiple address to the Mail instance. Then send the mail.

Upvotes: 1

Related Questions