How to forward an email with attachments using Python - python

I need to write a program that checks a gmail inbox using imaplib and forwards out emails that do not match a specific subject to a separate email. I can do this using stmplib but I do not know how to do it if the email that needs to be forwarded out and deleted contains an attachment. The following is my code that I need to update to be able to handle emails with attachments.
# Connect and login to email
imap = imaplib.IMAP4_SSL('imap.gmail.com')
imap.login('user#gmail.com','password')
imap.list()
imap.select('inbox')
smtp = smtplib.SMTP_SSL('smtp.gmail.com')
smtp.login('user#gmail.com','password')
try:
#Search and return sequential ids
result, data = imap.search(None,'ALL')
ids_list = data[0].split()
#print 'Total emails: '+str(len(ids_list))
latest_id = ids_list[-1]
#Process each email in inbox
for i in ids_list:
t, d = imap.fetch(i, '(RFC822)')
for res_part in d:
if isinstance(res_part, tuple):
text = res_part[1]
msg = email.message_from_string(text)
subject = msg['subject']
#print 'Subject: '+subject
message = get_txt(msg) #Retrieves email body text
#print message
if subject != 'The subject I\'m looking for': #Junk email
#print 'Sending to another email...'
smtp.sendmail('from#gmail.com', 'to#email.com', message)
imap.store(i, '+FLAGS', '\\Deleted')
imap.expunge()
else: #Email we need to process
#print 'Process this email'
except IndexError:
#Inbox is empty
Can somebody show me the proper way to accomplish this? Thanks!

To use this library you need an working SMTP server. You can use the one of you email provider, or you can setup one for yourself.
After this is done you cannot as a link, as example for an FTP server, on which you file is laying. This should also be supported with POP.

Related

Reply to email using imap email object

I have an email object of an unread email. How do I reply to the email using SMTP lib using the specific email object i.e. (if mail comes from flipkart, the particular mail has an object. I want to reply using that mail object)? I tried using msg['Reply_To'] and I got an error saying the email id is not valid. I did check the smtp documentation and couldn't find anything regarding the same. Here when I comment the [send to] and only add [reply to] the error is shown below. But when I uncomment the same the mail is sent but it is not sent as a reply. This shows that the email id is correct.
CODE:
def SendReply(self):
self.msg["from"] = self.From
self.msg["Cc"] = self.cc
self.msg["To"] = self.To
self.msg["Subject"] = self.Subject
self.Body = str(self.Body)
self.msg["Reply_To"] = self.Reply_To
self.msg.set_content(self.Body)
# print("Print this now", self.msg["Reply_To"])
if self.Attachments is not None:
with open(self.Attachments, "rb") as f:
file_data = f.read()
self.msg.add_attachment(
file_data,
maintype="application",
subtype="xlsx",
filename=self.Attachments,
)
with smtplib.SMTP("smtp.gmail.com", 587) as smtp:
smtp.starttls()
smtp.login(self.From, self.EmailPassword)
self.msg["Reply_To"] = self.Reply_To
smtp.send_message(self.msg)
return ("Mail sent Succesfully", True)
ERROR:
smtplib.SMTPRecipientsRefused:
{'None': (553,
b'5.1.3 The recipient address <None> is not a valid RFC-5321 address.
Learn\n5.1.3 more at\n5.1.3 https://support.google.com/mail/answer/6596 ij25-20020a170902ab5900b0016beceac426sm3191763plb.138 - gsmtp')}

Using Python smtplib to send to distribution lists

I'm using SMTPlib to automatically send out an email:
emailto = ['distro#email.com','me#email.com']
emailfrom = "me#email.com"
msg = MIMEMultipart('related')
msg['Subject'] = currentdate + " Subject"
msg['From'] = emailfrom
msg['To'] = ", ".join(emailto)
msgAlternative = MIMEMultipart('alternative')
msg.attach(msgAlternative)
msgAlternative.attach(msgText)
smtpObj = smtplib.SMTP('mail.email.com')
smtpObj.ehlo()
smtpObj.starttls()
smtpObj.sendmail(emailfrom, emailto, msg.as_string())
smtpObj.quit()
When I use this code I get the email, with distro#email.com in the "To:" line as well, but no one in distro#email.com gets it. I've sent to distribution lists before with no problem, but this specific one will not work. It is a fairly large list (~100 recipients)
Errors may pass unnoticed, because you are not checking the result of .sendmail() when sending to multiple addresses.
This method will not raise an exception if it succeeds to send the email to at least one recipient.
The important part of the docs is:
This method will return normally if the mail is accepted for at least
one recipient. Otherwise it will raise an exception.
If this method does not raise an exception, it returns a dictionary,
with one entry for each recipient that was refused. Each entry
contains a tuple of the SMTP error code and the accompanying error
message sent by the server.
Something like this should help to find the problem:
errors = smtpObj.sendmail(emailfrom, emailto, msg.as_string())
for recipient, (code, errmsg) in errors.items():
# ... print or log the error ...

Detect bounced emails in Python smtplib

I'm trying to catch all emails that bounced when sending them via smtplib in Python. I looked at this similar post which suggested adding an exception catcher, but I noticed that my sendmail function doesn't throw any exceptions even for fake email addresses.
Here is my send_email function which uses smtplib.
def send_email(body, subject, recipients, sent_from="myEmail#server.com"):
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = sent_from
msg['To'] = ", ".join(recipients)
s = smtplib.SMTP('mySmtpServer:Port')
try:
s.sendmail(msg['From'], recipients, msg.as_string())
except SMTPResponseException as e:
error_code = e.smtp_code
error_message = e.smtp_error
print("error_code: {}, error_message: {}".format(error_code, error_message))
s.quit()
Sample call:
send_email("Body-Test", "Subject-Test", ["fakejfdklsa#jfdlsaf.com"], "myemail#server.com")
Since I set the sender as myself, I am able to receive the email bounce report in my sender's inbox:
<fakejfdklsa#jfdlsaf.com>: Host or domain name not found. Name service error
for name=jfdlsaf.com type=A: Host not found
Final-Recipient: rfc822; fakejfdklsa#jfdlsaf.com
Original-Recipient: rfc822;fakejfdklsa#jfdlsaf.com
Action: failed
Status: 5.4.4
Diagnostic-Code: X-Postfix; Host or domain name not found. Name service error
for name=jfdlsaf.com type=A: Host not found
Is there a way to get the bounce message through Python?
import poplib
from email import parser
#breaks with if this is left out for some reason (MAXLINE is set too low by default.)
poplib._MAXLINE=20480
pop_conn = poplib.POP3_SSL('your pop server',port)
pop_conn.user(username)
pop_conn.pass_(password)
#Get messages from server:
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:
if "Undeliverable" in message['subject']:
print message['subject']
for part in message.walk():
if part.get_content_type():
body = str(part.get_payload(decode=True))
bounced = re.findall('[a-z0-9-_\.]+#[a-z0-9-\.]+\.[a-z\.]{2,5}',body)
if bounced:
bounced = str(bounced[0].replace(username,''))
if bounced == '':
break
print bounced
Hope this helps. This will check the contents of the mailbox for any undeliverable reports and read the message to find the email address that bounced.It then prints the result
You can use this function for gmail, basically checking the inbox that whether there is any email from Mail Delivery Subsystem with particular Text in body
def is_email_bounced(email, app_password, email_in_question):
obj = imaplib.IMAP4_SSL('imap.gmail.com',993)
obj.login(email, app_password)
obj.select() # Default Inbox
typ, data = obj.search(None, f'(TEXT "{email_in_question}") (Subject "Delivery Status Notification (Failure)")')
try: data.remove(b'')
except: pass
if len(data) >= 1: return True
else: return False
run this function after sending the email, and check whether email got bounced or not
e.g.
send_email("Body-Test", "Subject-Test", ["fakejfdklsa#jfdlsaf.com"], "myemail#server.com")
if is_email_bounced("youremail#gmail.com", "app_password", "email_in_ques_404#gmail.com"):
print("Email Bounced")
Change email Subject "Delivery Status Notification (Failure)" as per your email service provider, for this code, I've considered email provider as gmail

Send email to multiple addresses (using cc) from different domains

I've made a little Python script that sends emails using smtplib.
For example, I have an email that needs to be sent to n users (via To: field), but I also need to send this email to m other users, via Cc: field.
Obviously those n + m email addresses are from different domains (#mydomain, #gmail, #hotmail, #whatever). The emails are correctly delivered to each address if I put the email addresses in the To: field, but the same thing doesn't happen if I put the emails in the Cc: field....
For example
FROM: me#mydomain.com
TO: alice#mydomain.com, bob#gmail.com, mallory#hotmail.com
CC: john#mydomain.com, robert#yahoo.com, clara#gmail.com
note that the email is sent using a #mydomain.com account. The addresses in the TO: list correctly receive the email, while only john#mydomain.com, from the CC: list, get the email..
It seems that the CC field works only with same-domain-email... Any idea?
Anyway, this is the code:
msg = MIMEText(mailContent)
msg["Subject"] = "This is the subject"
msg["From"] = "me#mydomain.com"
toEmails = ["alice#mydomain.com", "bob#gmail.com", "mallory#hotmail.com"]
ccEmails = ["john#mydomain.com", "robert#yahoo.com", "clara#gmail.com"]
msg["To"] = ",".join(toEmails)
msg["Cc"] = ",".join(ccEmails)
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login("me#mydomain.com", "password")
server.sendmail("me#mydomain.com", toEmails, msg.as_string())
server.quit()
Thanks
change this line
server.sendmail("me#mydomain.com", toEmails+ccEmails, msg.as_string())

Send Outlook Email Via Python?

I am using Outlook 2003.
What is the best way to send email (through Outlook 2003) using Python?
import win32com.client as win32
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = 'To address'
mail.Subject = 'Message subject'
mail.Body = 'Message body'
mail.HTMLBody = '<h2>HTML Message body</h2>' #this field is optional
# To attach a file to the email (optional):
attachment = "Path to the attachment"
mail.Attachments.Add(attachment)
mail.Send()
Will use your local outlook account to send.
Note if you are trying to do something not mentioned above, look at the COM docs properties/methods: https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/mailitem-object-outlook. In the code above, mail is a MailItem Object.
For a solution that uses outlook see TheoretiCAL's answer.
Otherwise, use the smtplib that comes with python. Note that this will require your email account allows smtp, which is not necessarily enabled by default.
SERVER = "smtp.example.com"
FROM = "yourEmail#example.com"
TO = ["listOfEmails"] # must be a list
SUBJECT = "Subject"
TEXT = "Your Text"
# Prepare actual message
message = """From: %s\r\nTo: %s\r\nSubject: %s\r\n\
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
import smtplib
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
EDIT: this example uses reserved domains like described in RFC2606
SERVER = "smtp.example.com"
FROM = "johnDoe#example.com"
TO = ["JaneDoe#example.com"] # must be a list
SUBJECT = "Hello!"
TEXT = "This is a test of emailing through smtp of example.com."
# Prepare actual message
message = """From: %s\r\nTo: %s\r\nSubject: %s\r\n\
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
import smtplib
server = smtplib.SMTP(SERVER)
server.login("MrDoe", "PASSWORD")
server.sendmail(FROM, TO, message)
server.quit()
For it to actually work with gmail, Mr. Doe will need to go to the options tab in gmail and set it to allow smtp connections.
Note the addition of the login line to authenticate to the remote server. The original version does not include this, an oversight on my part.
I wanted to send email using SMTPLIB, its easier and it does not require local setup. After other answers were not directly helpful, This is what i did.
Open Outlook in a browser; Go to the top right corner, click the gear icon for Settings, Choose 'Options' from the appearing drop-down list.
Go to 'Accounts', click 'Pop and Imap',
You will see the option: "Let devices and apps use pop",
Choose Yes option and Save changes.
Here is the code there after; Edit where neccesary.
Most Important thing is to enable POP and the server code herein;
import smtplib
body = 'Subject: Subject Here .\nDear ContactName, \n\n' + 'Email\'s BODY text' + '\nYour :: Signature/Innitials'
try:
smtpObj = smtplib.SMTP('smtp-mail.outlook.com', 587)
except Exception as e:
print(e)
smtpObj = smtplib.SMTP_SSL('smtp-mail.outlook.com', 465)
#type(smtpObj)
smtpObj.ehlo()
smtpObj.starttls()
smtpObj.login('me#outlook.com', "password")
smtpObj.sendmail('sender#outlook.com', 'recipient#gmail.com', body) # Or recipient#outlook
smtpObj.quit()
pass
using pywin32:
from win32com.client import Dispatch
session = Dispatch('MAPI.session')
session.Logon('','',0,1,0,0,'exchange.foo.com\nUserName');
msg = session.Outbox.Messages.Add('Hello', 'This is a test')
msg.Recipients.Add('Corey', 'SMTP:corey#foo.com')
msg.Send()
session.Logoff()
A simple solution for Office 365 is
from O365 import Message
html_template = """
<html>
<head>
<title></title>
</head>
<body>
{}
</body>
</html>
"""
final_html_data = html_template.format(df.to_html(index=False))
o365_auth = ('sender_username#company_email.com','Password')
m = Message(auth=o365_auth)
m.setRecipients('receiver_username#company_email.com')
m.setSubject('Weekly report')
m.setBodyHTML(final)
m.sendMessage()
Here df is a dataframe converted to an html Table, which is being injected into html_template
This is a pretty old question but there is one more solution. The current Outlook SMTP server is (as of 2022):
Host: smtp.office365.com
Port: 587 (for TLS)
Probably the easiest and cleanest solution is to use Red Mail that has these already set:
pip install redmail
Then:
from redmail import outlook
outlook.user_name = "example#hotmail.com"
outlook.password = "<MY PASSWORD>"
outlook.send(
receivers=["you#example.com"],
subject="An example",
text="Hi, this is an example."
)
Red Mail supports all sorts of advanced features:
HTML and text bodies
Attachments from various types
Embedded images
It also has Jinja support
Links:
Documentation: https://red-mail.readthedocs.io/en/latest/
Source code: https://github.com/Miksus/red-mail
Disclaimer: I'm the author
Other than win32, if your company had set up you web outlook, you can also try PYTHON REST API, which is officially made by Microsoft. (https://msdn.microsoft.com/en-us/office/office365/api/mail-rest-operations)
This was one I tried using Win32:
import win32com.client as win32
import psutil
import os
import subprocess
import sys
# Drafting and sending email notification to senders. You can add other senders' email in the list
def send_notification():
outlook = win32.Dispatch('outlook.application')
olFormatHTML = 2
olFormatPlain = 1
olFormatRichText = 3
olFormatUnspecified = 0
olMailItem = 0x0
newMail = outlook.CreateItem(olMailItem)
newMail.Subject = sys.argv[1]
#newMail.Subject = "check"
newMail.BodyFormat = olFormatHTML #or olFormatRichText or olFormatPlain
#newMail.HTMLBody = "test"
newMail.HTMLBody = sys.argv[2]
newMail.To = "xyz#abc.com"
attachment1 = sys.argv[3]
attachment2 = sys.argv[4]
newMail.Attachments.Add(attachment1)
newMail.Attachments.Add(attachment2)
newMail.display()
# or just use this instead of .display() if you want to send immediately
newMail.Send()
# Open Outlook.exe. Path may vary according to system config
# Please check the path to .exe file and update below
def open_outlook():
try:
subprocess.call(['C:\Program Files\Microsoft Office\Office15\Outlook.exe'])
os.system("C:\Program Files\Microsoft Office\Office15\Outlook.exe");
except:
print("Outlook didn't open successfully")
#
# Checking if outlook is already opened. If not, open Outlook.exe and send email
for item in psutil.pids():
p = psutil.Process(item)
if p.name() == "OUTLOOK.EXE":
flag = 1
break
else:
flag = 0
if (flag == 1):
send_notification()
else:
open_outlook()
send_notification()

Categories