I am working with the below code to send emails to customer and as you guys know there is daily sending limits set by different companies.
What I want to achieve is to change email credentials before reaching limit but continue with the recipient list.
For example: I have 5 email credentials each email id will 50 emails and will change automatically.
Code I work with:
import smtplib
li = ["xxxxx#gmail.com", "yyyyy#gmail.com"]
for dest in li:
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login("sender_email_id", "sender_email_id_password")
message = "Message_you_need_to_send"
s.sendmail("sender_email_id", dest, message)
s.quit()
Get the index of the destination in its list, and transform that into an index into the credentials list.
for i, dest in enumerate(li):
if i//50 >= len(credentials):
print("Ran out of credentials")
break
creds = credentials[i//50]
// use credentials to send mail
clients = ["xxxxx#gmail.com", "yyyyy#gmail.com"]
chunk_size = 50
chunks = (clients[i:i+chunk_size] for i in range(0,len(clients), chunk_size))
creds = [("sender_email_id_1", "sender_email_id_password_1"), ("sender_email_id_2", "sender_email_id_password_2")]
for i,chunk in enumerate(chunks):
try:
user, pw = creds[i]
except IndexError:
print("Ran out of credentials")
raise
for client in chunk:
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login(user, pw)
message = "Message_you_need_to_send"
s.sendmail(user, client, message)
s.quit()
If you'd rather not throttle yourself, but would like to keep trying to send as many emails as possible:
clients = ["xxxxx#gmail.com", "yyyyy#gmail.com"]
creds = [("sender_email_id_1", "sender_email_id_password_1"), ("sender_email_id_2", "sender_email_id_password_2")]
for cred,client in zip(itertools.cycle(creds), clients):
user, pw = creds
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login(user, pw)
message = "Message_you_need_to_send"
s.sendmail(user, client, message)
s.quit()
The following uses a generator nextcred to loop over the credentials in a list until you have sent all the messages. It wraps back to the first pair after you have looped over the whole list.
We use one pair to send 50 messages, then log out and back in with the next pair of credentials.
import smtplib
def nextcred():
credentials = [('user', 'password'), ('plugh', 'xyzzy'), ('alfred', 'what me worry?')]
while True:
for tup in credentials:
yield tup
li = ["xxxxx#gmail.com", "yyyyy#gmail.com"]
cred = nextcred()
s = None
for idx, dest in enumerate(li):
if (idx % 50) == 0:
if s:
s.quit()
account, passw = next(cred)
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login(account, passw)
message = "Message_you_need_to_send"
s.sendmail("sender_email_id", dest, message)
s.quit()
As an aside, I hope you are not really constructing your SMTP messages by pasting together strings. The Python email library takes care of a mound of corner cases and complications which are hard to get right and impossible to guess how to solve if you are not intimately familiar with SMTP and MIME.
Related
I'm trying to reply to an email based on the following criteria:
Scan the inbox for unseen mails with specific Subject content, if there is mails that satisfy those criteria then: send back an reply message to the sender saying "something", if those criteria are not met then: send back an reply message to the sender saying "something".
This is what i came up with so far:
import imaplib
import email
import smtplib
username = 'sample#gmail.com'
password = 'xxxx'
imap_server = imaplib.IMAP4_SSL('smtp.gmail.com')
imap_server.login(username, password)
imap_server.select('INBOX')
result, data = imap_server.search(None, '(UNSEEN)')
email_ids = data[0].split()
for email_id in email_ids:
result, data = imap_server.fetch(email_id, "(RFC822)")
raw_email = data[0][1]
email_message = email.message_from_bytes(raw_email)
subject = email_message["Subject"]
if subject == "SOME SPECIFIC CONTENT":
reply = email.message.EmailMessage()
reply["To"] = email_message["From"]
reply["Subject"] = "Re: " + email_message["Subject"]
reply["In_Reply-To"] = email_message["From"]
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(username, password)
server.sendmail(username, reply["In_Reply-To"], 'Subject: Criteria met\n\nThank you.')
server.quit()
else:
reply = email.message.EmailMessage()
reply['To'] = email_message['From']
reply['Subject'] = "RE:" + email_message['Subject']
reply["In_Reply-To"] = email_message["From"]
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(username, password)
server.sendmail(username, reply["In_Reply-To"], 'Subject: Criteria not met\n\Thank you.')
print('Sending email')
server.quit()
imap_server.close()
It sends the email but without the desired thread, just sends a new email and not actually replying back to the sender.
Any suggestion on how to modify the code so it actually send an reply with the desired thread?
Thank you in advance.
Like the comment mentions, you should use the Message-Id of the original message, not the sender address.
Also, you should obey Reply-To: and add References:.
reply = email.message.EmailMessage()
reply["To"] = email_message["Reply-To"] or email_message["From"]
reply["Subject"] = "Re: " + email_message["Subject"]
reply["In_Reply-To"] = email_message["Message-Id"]
reply["References"] = (email_message["References"] or "") + " " + email_message["Message-Id"]
Properly speaking, the References: header should be trimmed from the middle if it's too long.
Some vendors have their own nonstandard threading extensions; in particular, Microsoft's Thread-Id: etc headers are probably best ignored.
I want to get the last 10 received gmails with python.
Currently I have this code but it only returns a limited number of emails and it manipulates pop3 directly, which makes it unnecessary long.
Source of the code: https://www.code-learner.com/python-use-pop3-to-read-email-example/
import poplib
import smtplib, ssl
def guess_charset(msg):
# get charset from message object.
charset = msg.get_charset()
# if can not get charset
if charset is None:
# get message header content-type value and retrieve the charset from the value.
content_type = msg.get('Content-Type', '').lower()
pos = content_type.find('charset=')
if pos >= 0:
charset = content_type[pos + 8:].strip()
return charset
def decode_str(s):
value, charset = decode_header(s)[0]
if charset:
value = value.decode(charset)
return value
# variable indent_number is used to decide number of indent of each level in the mail multiple bory part.
def print_info(msg, indent_number=0):
if indent_number == 0:
# loop to retrieve from, to, subject from email header.
for header in ['From', 'To', 'Subject']:
# get header value
value = msg.get(header, '')
if value:
# for subject header.
if header=='Subject':
# decode the subject value
value = decode_str(value)
# for from and to header.
else:
# parse email address
hdr, addr = parseaddr(value)
# decode the name value.
name = decode_str(hdr)
value = u'%s <%s>' % (name, addr)
print('%s%s: %s' % (' ' * indent_number, header, value))
# if message has multiple part.
if (msg.is_multipart()):
# get multiple parts from message body.
parts = msg.get_payload()
# loop for each part
for n, part in enumerate(parts):
print('%spart %s' % (' ' * indent_number, n))
print('%s--------------------' % (' ' * indent_number))
# print multiple part information by invoke print_info function recursively.
print_info(part, indent_number + 1)
# if not multiple part.
else:
# get message content mime type
content_type = msg.get_content_type()
# if plain text or html content type.
if content_type=='text/plain' or content_type=='text/html':
# get email content
content = msg.get_payload(decode=True)
# get content string charset
charset = guess_charset(msg)
# decode the content with charset if provided.
if charset:
content = content.decode(charset)
print('%sText: %s' % (' ' * indent_number, content + '...'))
else:
print('%sAttachment: %s' % (' ' * indent_number, content_type))
# input email address, password and pop3 server domain or ip address
email = 'yourgmail#gmail.com'
password = 'yourpassword'
# connect to pop3 server:
server = poplib.POP3_SSL('pop.gmail.com')
# open debug switch to print debug information between client and pop3 server.
server.set_debuglevel(1)
# get pop3 server welcome message.
pop3_server_welcome_msg = server.getwelcome().decode('utf-8')
# print out the pop3 server welcome message.
print(server.getwelcome().decode('utf-8'))
# user account authentication
server.user(email)
server.pass_(password)
# stat() function return email count and occupied disk size
print('Messages: %s. Size: %s' % server.stat())
# list() function return all email list
resp, mails, octets = server.list()
print(mails)
# retrieve the newest email index number
#index = len(mails)
index = 3
# server.retr function can get the contents of the email with index variable value index number.
resp, lines, octets = server.retr(index)
# lines stores each line of the original text of the message
# so that you can get the original text of the entire message use the join function and lines variable.
msg_content = b'\r\n'.join(lines).decode('utf-8')
# now parse out the email object.
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
import poplib
# parse the email content to a message object.
msg = Parser().parsestr(msg_content)
print(len(msg_content))
# get email from, to, subject attribute value.
email_from = msg.get('From')
email_to = msg.get('To')
email_subject = msg.get('Subject')
print('From ' + email_from)
print('To ' + email_to)
print('Subject ' + email_subject)
for part in msg.walk():
if part.get_content_type():
body = part.get_payload(decode=True)
print_info(msg, len(msg))
# delete the email from pop3 server directly by email index.
# server.dele(index)
# close pop3 server connection.
server.quit()
I also tried this code but it didn't work:
import imaplib, email, base64
def fetch_messages(username, password):
messages = []
conn = imaplib.IMAP4_SSL("imap.gmail.com", 993)
conn.login(username, password)
conn.select()
typ, data = conn.uid('search', None, 'ALL')
for num in data[0].split():
typ, msg_data = conn.uid('fetch', num, '(RFC822)')
for response_part in msg_data:
if isinstance(response_part, tuple):
messages.append(email.message_from_string(response_part[1]))
typ, response = conn.store(num, '+FLAGS', r'(\Seen)')
return messages
and this also didn't work for me...
import poplib
from email import parser
pop_conn = poplib.POP3_SSL('pop.gmail.com')
pop_conn.user('#gmail.com')
pop_conn.pass_('password')
messages = [pop_conn.retr(i) for i in range(1, len(pop_conn.list()[1]) + 1)]
# Concat message pieces:
messages = ["\n".join(mssg[1]) for mssg in messages]
#Parse message intom an email object:
messages = [parser.Parser().parsestr(mssg) for mssg in messages]
for message in messages:
print(message['subject'])
print(message['body'])
I managed to solve it, the only issue is that it marks as read every unread email, here is the code I used:
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
email = input('Email: ')
password = input('Password: ')
mail.login(email+'#gmail.com', password)
mail.list()
# Out: list of "folders" aka labels in gmail.
mail.select("inbox") # connect to inbox.
result, data = mail.search(None, "ALL")
ids = data[0] # data is a list.
id_list = ids.split() # ids is a space separated string
latest_email_id = id_list[-1] # get the latest
# fetch the email body (RFC822) for the given ID
result, data = mail.fetch(latest_email_id, "(RFC822)")
raw_email = data[0][1] # here's the body, which is raw text of the whole email
# including headers and alternate payloads
import email
email_message = email.message_from_string(str(raw_email))
print (email_message['To'])
print (email.utils.parseaddr(email_message['From'])) # for parsing "Yuji Tomita" <yuji#grovemade.com>
print (email_message.items()) # print all headers
# note that if you want to get text content (body) and the email contains
# multiple payloads (plaintext/ html), you must parse each message separately.
# use something like the following: (taken from a stackoverflow post)
def get_first_text_block(self, email_message_instance):
maintype = email_message_instance.get_content_maintype()
if maintype == 'multipart':
for part in email_message_instance.get_payload():
if part.get_content_maintype() == 'text':
return part.get_payload()
elif maintype == 'text':
return email_message_instance.get_payload()
https://developers.google.com/gmail/api/quickstart/python is the preferred way:
from gmail.gmail import gmail_auth, ListThreadsMatchingQuery
service = gmail_auth()
threads = ListThreadsMatchingQuery(service, query=query)
where:
def ListThreadsMatchingQuery(service, user_id='me', query=''):
"""List all Threads of the user's mailbox matching the query.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
query: String used to filter messages returned.
Eg.- 'label:UNREAD' for unread messages only.
Returns:
List of threads that match the criteria of the query. Note that the returned
list contains Thread IDs, you must use get with the appropriate
ID to get the details for a Thread.
"""
try:
response = service.users().threads().list(userId=user_id, q=query).execute()
threads = []
if 'threads' in response:
threads.extend(response['threads'])
while 'nextPageToken' in response:
page_token = response['nextPageToken']
response = service.users().threads().list(userId=user_id, q=query,
pageToken=page_token).execute()
threads.extend(response['threads'])
return threads
except errors.HttpError as error:
raise error
You should try easyimap lib to get a list of e-mails, I'm not sure if works with pop3.
Code example:
import easyimap
host = 'imap.gmail.com'
user = 'you#example.com'
password = 'secret'
mailbox = 'INBOX.subfolder'
imapper = easyimap.connect(host, user, password, mailbox)
email_quantity = 10
emails_from_your_mailbox = imapper.listids(limit=email_quantity)
imapper.quit()
I'm running a script that will both fetch emails (imaplib) and will send alerts back to my personal email using smtplib. I am running it on my raspberry pi 24/7 and so when I am not at home I want to check that the program is still running fine.
Here are the functions:
def get_alert():
global subject
user = alert_email
password = alert_email_password
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(user, password)
mail.list()
mail.select('inbox')
result, data = mail.uid('search', None, 'UNSEEN')
i = len(data[0].split())
for x in range(i):
latest_email_uid = data[0].split()[x]
result, email_data = mail.uid('fetch', latest_email_uid, '(RFC822)')
raw_email = email_data[0][1]
raw_email_string = raw_email.decode('utf-8')
email_message = email.message_from_string(raw_email_string)
subject = str(email.header.make_header(email.header.decode_header(email_message['Subject'])))
print(subject)
return subject
mail.logout()
def send_alert(subj, msg):
server = smtplib.SMTP('smtp.gmail.com:587')
server.ehlo()
server.starttls()
server.login(alert_email, alert_email_password)
message = 'Subject: {}\n\n{}'.format(subj, msg)
server.sendmail(from_addr=alert_email, to_addrs=personal_email, msg=message)
server.quit()
Here's the loop:
while True:
get_alert()
if subject == 'check': # check that program is still running
send_alert(subj='still running', msg='still running')
if subject == ...
So what happens is that it will get the 'check' email perfectly, and prints it out only once as it should. Yet every loop sends me 'still running' every time it runs despite the email being marked as read and having no more unread emails. Shouldn't subject be None if there are no unread emails?? I've tried manually setting subject to none and marking all emails as read after returning subject. Not sure what is happening, if you can help I greatly appreciate. this has been driving me nuts all day :/
I'm a very new Python coder so please don't go too harsh on me, thanks.
I'm trying to make an emailer using smtplib and I'm having trouble with handing the users credentials to Google.
Full code:
mailcheck = input("This mailer is only for gmail, want to continue? (yes or no)")
# GMAIL SMTP INFORMATION
if mailcheck == "yes" or mailcheck == "Yes" or mailcheck == "y":
smtp_server = 'smtp.gmail.com'
port = 587
set_server = "gmail"
else:
print("Hey, sorry this program is only for Gmail")
#USER CREDENTIALS + RECEIVER
username = input("Google Email?")
password = input("Password?")
receiver = input("Who to send it to?")
subject = input("Subject of the email?")
econtents = input("What to put in the email?")
amount = input("Amount of emails to send?")
credentials = username + password
global d1
global d2
try:
server = smtplib.SMTP(smtp_server, port)
server.ehlo()
server.starttls()
server.login(username, password)
print("Sending" + amount, "to", receiver)
for i in range(1, int(amount) + 1):
message = "From" + credentials[0] + '\nSubject' + subject + '\n' + econtents
time.sleep(random.uniform(d1, d2))
server.sendmail(credentials[0], receiver, message)
print("\nEmail Sent!")
else:
print("Finished sending emails")
except smtplib.SMTPRecipientsRefused:
print("Recipient refused. Invalid email address?")
except smtplib.SMTPAuthenticationError:
print("Unable to authenticate with server. Ensure the details are correct.")
except smtplib.SMTPServerDisconnected:
print("Server disconnected for an unknown reason.")
except smtplib.SMTPConnectError:
print("Unable to connect to server.")
The error :
Unable to authenticate with server. Ensure the details are correct.
This means it went wrong with the login process. It should be going wrong somewhere in this part:
#USER CREDENTIALS + RECEIVER
username = input("Google Email?")
password = input("Password?")
receiver = input("Who to send it to?")
subject = input("Subject of the email?")
econtents = input("What to put in the email?")
amount = input("Amount of emails to send?")
credentials = username + password
global d1
global d2
try:
server = smtplib.SMTP(smtp_server, port)
server.ehlo()
server.starttls()
server.login(username, password)
print("Sending" + amount, "to", receiver)
for i in range(1, int(amount) + 1):
message = "From" + credentials[0] + '\nSubject' + subject + '\n' + econtents
time.sleep(random.uniform(d1, d2))
server.sendmail(credentials[0], receiver, message)
print("\nEmail Sent!")
I think it's because of the credentials = username + password which doesn't work, but I have no idea how I'd fix it.
If anyone knows what I'd have to change to fix this that'd be great!
Instead of adding those two strings, you're meaning to put them in an array. In Python, that's either
credentials = [username, password]
or
credentials = list(username, password)
But that doesn't seem to be your issue. Your issue is related to the login() function as you get the SMTPAuthenticationError exception. The smtplib documentation says that after running .starttls(), you should run .ehlo() again. Try to run that before logging in. Additionally, you could try to generate an SSL instance on port 465.
(Ctrl+F for .starttls())
https://docs.python.org/2/library/smtplib.html
I have the following script for sending mails using python:
import smtplib
from celery import Celery
from email.mime.text import MIMEText
from KerbalStuff.config import _cfg, _cfgi, _cfgb
app = Celery("tasks", broker=_cfg("redis-connection"))
def chunks(l, n):
""" Yield successive n-sized chunks from l.
"""
for i in range(0, len(l), n):
yield l[i:i+n]
#app.task
def send_mail(sender, recipients, subject, message, important=False):
if _cfg("smtp-host") == "":
return
smtp = smtplib.SMTP(host=_cfg("smtp-host"), port=_cfgi("smtp-port"))
if _cfgb("smtp-tls"):
smtp.starttls()
if _cfg("smtp-user") != "":
smtp.login(_cfg("smtp-user"), _cfg("smtp-password"))
message = MIMEText(message)
if important:
message['X-MC-Important'] = "true"
message['X-MC-PreserveRecipients'] = "false"
message['Subject'] = subject
message['From'] = sender
if len(recipients) > 1:
message['Precedence'] = 'bulk'
for group in chunks(recipients, 100):
if len(group) > 1:
message['To'] = "undisclosed-recipients:;"
else:
message['To'] = ";".join(group)
print("Sending email from {} to {} recipients".format(sender, len(group)))
smtp.sendmail(sender, group, message.as_string())
smtp.quit()
When I use the script, I see that the mail gets delivered to everybody in the group but all the recipients appear in the To field while it should show your own adress in the To field and all the others in the Bcc field.
Is there any error in the script? Initially I thought that this might be an issue with my mail server.
On this line
else:
message['To'] = ";".join(group)
You're concatenating the recipients to the group, and into the To field of your email, and not the Bcc field. Take a look at this StackOverflow Post example