I want to forward an Outlook email to a specific folder (not Inbox) using python. I'm using win32com.
When I Forward then Move, my email does appear in the specific folder, but when I look in my Outlook the Datetime and From are not showing.
How do I get the Datetime and From onto the forward/moved email?
import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
root_folder = outlook.Folders.Item(1)
sub_folder1 = root.Folders['Project1']
next_level = sub_folder1.Folders['ProjectA']
msg = next_level.Items
msgs = msg.GetLast()
dest = sub_folder1.Folders['ProjectB']
NewMsg = msgs.Forward()
NewMsg.Move(dest)
This is because you're making a draft of the original received email.
To simply move the email remove the NewMsg = msgs.Forward() line and replace NewMsg.Move(dest) with msgs.Move(dest).
import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
root_folder = outlook.Folders.Item(1)
sub_folder1 = root_folder.Folders['Project1']
next_level = sub_folder1.Folders['ProjectA']
msg = next_level.Items
msgs = msg.GetLast()
dest = sub_folder1.Folders['ProjectB']
msgs.Move(dest)
If you wish to make an actual copy and move the new item to the second folder, replace your ending code with
msgcopy = msgs.Copy()
dest = sub_folder1.Folders['ProjectB']
msgcopy.Move(dest)
Related
This code takes email pdf attachments, download it, merge to one pdf file and send further.
Now it takes all emails which are marked with specific category in that inbox, so it merge all pdf's from all emails to one file.
But I want that it take emails one by one, that after download pdf's from one email it will merge and send them, delete them from folder and just after that it will take second email.
How to make such loop for this code?
import datetime
import os
import win32com.client as win32
from PyPDF2 import PdfFileMerger
from pathlib import Path
path = ('C:\\Users\\Desktop\\Work')
today = datetime.date.today()
outlook = win32.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
subFolder = inbox.Folders("Test")
messages = subFolder.Items
def save_attachments(subject):
for message in messages:
if message.Categories == "Red Category":
for attachment in message.Attachments:
print(attachment.FileName)
attachment.SaveAsFile(os.path.join(path, str(attachment)))
if __name__ == "__main__":
save_attachments('PB report - next steps')
#Merge PDF's
merger = PdfFileMerger()
path_to_files = r'C:\Users\Desktop\Work/'
for root, dirs, file_names in os.walk(path_to_files):
for file_name in file_names:
merger.append(path_to_files + file_name)
merger.write(r"C:\Users\Desktop\Work\merged.pdf")
merger.close()
#Send PDF with outlook
# construct Outlook application instance
olApp = win32.Dispatch('Outlook.Application')
olNS = olApp.GetNameSpace('MAPI')
# construct the email item object
mailItem = olApp.CreateItem(0)
mailItem.Subject = 'Test'
mailItem.BodyFormat = 1
mailItem.Body = "Pdf merged"
mailItem.To = 'email'
path = (os.path.join('C:\\Users\\Desktop\\Work\\merged.pdf'))
mailItem.Attachments.Add(path)
mailItem.Display()
mailItem.Save()
mailItem.Send()
#Delete PDF's from folder
[f.unlink() for f in Path("C:\\Users\\Desktop\\Work").glob("*") if f.is_file()]
Iterating over all items in the folder is not really a good idea:
for message in messages:
if message.Categories == "Red Category":
Instead, you need to use the Find/FindNext or Restrict methods of the Items class from the Outlook object model. So, in that case you will get all items that correspond to your search criteria and iterate over them only. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Second, there is no need to create a new Outlook Application instance:
# construct Outlook application instance
olApp = win32.Dispatch('Outlook.Application')
olNS = olApp.GetNameSpace('MAPI')
Re-use the existing application instance instead. Moreover, Outlook is a singleton, you can't have two instances running at the same time.
Third, there is no need to display and save the item created before sending:
mailItem.Attachments.Add(path)
mailItem.Send()
I am able to send email to different recipients on outlook with below script for single attachment, but if I try to send different attachments to each user using for loop, then it fails.
Currently the script is using attachment = r'C:\Users\roy\Royfile.csv'. But I want attachment = file, so that the attachment changes in each for loop for different users. This part is not working.
Different files for different users, example Royfile.csv below. But there are 50 more such files.
Folder FolderOwner EmailAddress AttachmentPath
C:\folder1\ Roy Roy#gmail.com Royfile.csv
D:\folder2\ Roy Roy#gmail.com Royfile.csv
2nd file in same folder Jackfile.csv:
Folder FolderOwner EmailAddress AttachmentPath
C:\folder3\ Jack Jack#gmail.com Jackfile.csv
D:\folder4\ Jack Jack#gmail.com Jackfile.csv
3rd file for example Mandyfile.csv. And same way total 50 files for 50 users in same folder.
Folder FolderOwner EmailAddress AttachmentPath
C:\folder5\ Mandy Mandy#gmail.com Mandyfile.csv
D:\folder6\ Mandy Mandy#gmail.com Mandyfile.csv
Python Script
import glob, as
import win32com.client as win32
import pandas as pd
for file in glob.glob("*file.csv"):
print(file)
email_list = pd.read_csv(file)
names = email_list['FolderOwner']
emails = email_list['EmailAddress']
attachments = email_list['AttachmentPath']
for i in range(len(emails)):
print(file)
name = names[i]
email = emails[i]
attachment = r'{}.csv'.format(attachments)
with open(attachment, 'r') as my_attachment:
myfile = my_attachment.read()
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = email
mail.Subject = 'Message subject'
mail.Body = 'Hello ' + name
mail.Attachments.Add(attachment)
mail.Send()
break
Current output of the script if I remove the attachment part:
Royfile.csv
Royfile.csv
Jackfile.csv
Jackfile.csv
Mandyfile.csv
Mandyfile.csv
...
..
.
Struggling now with what needs to be for attachment = ???. So that each file gets sent to 50 users.
I don't know how your files named how they are distributed in different folders, try to put their all names along with paths in excel sheet in one column and iterate through them the way you are doing for names and mails
attachment = r'{}.csv'.format(filepaths from excel sheet)
with open(attachment, 'r') as my_attachment:
myfile = my_attachment.read()
Found answer for my question finally, below is full code.
The error was coming, as there was PATH missing.
win32com lib need full path even if the script is running in same folder as the attachments.
works perfectly now. :)
import glob, as
import win32com.client as win32
import pandas as pd
for file in glob.glob("*file.csv"):
print(file)
email_list = pd.read_csv(file)
names = email_list['FolderOwner']
emails = email_list['EmailAddress']
attachments = email_list['AttachmentPath']
PATH = "C:\\Users\\roy\\myfolder\\"
for i in range(len(emails)):
print("Sending email with " + file)
name = names[i]
email = emails[i]
attachment = attachments[i]
attachment1 = PATH + attachment
with open(attachment1, 'r') as my_attachment:
myfile = my_attachment.read()
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = email
mail.Subject = 'Message subject'
mail.Body = 'Hello ' + name
mail.Attachments.Add(attachment1)
mail.Send()
break
I am able to send email to different recipients on outlook with below script for single attachment, but if I try to send different attachments to each user using for loop, then it fails.
Currently the script is using attachment = r'C:\Users\roy\Royfile.csv'. But I want attachment = file, so that the attachment changes in each for loop for different users. This part is not working.
Different files for different users, example Royfile.csv below. But there are 50 more such files.
Folder FolderOwner EmailAddress AttachmentPath
C:\folder1\ Roy Roy#gmail.com Royfile.csv
D:\folder2\ Roy Roy#gmail.com Royfile.csv
2nd file in same folder Jackfile.csv:
Folder FolderOwner EmailAddress AttachmentPath
C:\folder3\ Jack Jack#gmail.com Jackfile.csv
D:\folder4\ Jack Jack#gmail.com Jackfile.csv
3rd file for example Mandyfile.csv. And same way total 50 files for 50 users in same folder.
Folder FolderOwner EmailAddress AttachmentPath
C:\folder5\ Mandy Mandy#gmail.com Mandyfile.csv
D:\folder6\ Mandy Mandy#gmail.com Mandyfile.csv
Python Script
import glob, as
import win32com.client as win32
import pandas as pd
for file in glob.glob("*file.csv"):
print(file)
email_list = pd.read_csv(file)
names = email_list['FolderOwner']
emails = email_list['EmailAddress']
attachments = email_list['AttachmentPath']
for i in range(len(emails)):
print(file)
name = names[i]
email = emails[i]
attachment = r'{}.csv'.format(attachments)
with open(attachment, 'r') as my_attachment:
myfile = my_attachment.read()
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = email
mail.Subject = 'Message subject'
mail.Body = 'Hello ' + name
mail.Attachments.Add(attachment)
mail.Send()
break
Current output of the script if I remove the attachment part:
Royfile.csv
Royfile.csv
Jackfile.csv
Jackfile.csv
Mandyfile.csv
Mandyfile.csv
...
..
.
Struggling now with what needs to be for attachment = ???. So that each file gets sent to 50 users.
I don't know how your files named how they are distributed in different folders, try to put their all names along with paths in excel sheet in one column and iterate through them the way you are doing for names and mails
attachment = r'{}.csv'.format(filepaths from excel sheet)
with open(attachment, 'r') as my_attachment:
myfile = my_attachment.read()
Found answer for my question finally, below is full code.
The error was coming, as there was PATH missing.
win32com lib need full path even if the script is running in same folder as the attachments.
works perfectly now. :)
import glob, as
import win32com.client as win32
import pandas as pd
for file in glob.glob("*file.csv"):
print(file)
email_list = pd.read_csv(file)
names = email_list['FolderOwner']
emails = email_list['EmailAddress']
attachments = email_list['AttachmentPath']
PATH = "C:\\Users\\roy\\myfolder\\"
for i in range(len(emails)):
print("Sending email with " + file)
name = names[i]
email = emails[i]
attachment = attachments[i]
attachment1 = PATH + attachment
with open(attachment1, 'r') as my_attachment:
myfile = my_attachment.read()
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = email
mail.Subject = 'Message subject'
mail.Body = 'Hello ' + name
mail.Attachments.Add(attachment1)
mail.Send()
break
I have just created a few files and zipped them up, then begun an email to send it. I'ts probably simple, but I haven't been able to figure out how to specify a file by path to attach. Feeding the filepath alone doesn't seem to work?
ZipName = 'Order'+OrderNumber+'.zip'
zip = zipfile.ZipFile(ZipName, 'a', 8)
for file in os.listdir(filepath_out):
if file.endswith(".epw"):
zip.write(file)
zip.close()
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
#mail.From = 'sales#c1.com'
mail.To = 'support#c2.com'
mail.Subject = 'Files for Order ' + OrderNumber
mail.HtmlBody = ""
mail.Attachments.Add(ZipName)
mail.Display(True)
It's off topic but related; is there an easy way to specify a non-default "from" email address? "From" doesn't seem to be a property and "Sender" doesn't change anything.
Attachments.Add takes a fully qualified file name (e.g. c:\temp\order1.zip), not just a file name.
Does anyone know how to add an email signature to an email using win32com?
import win32com.client as win32
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = 'TO'
mail.Subject = 'SUBJECT'
mail.HTMLbody = 'BODY'
mail.send
A fully functional e-mailer function with signature included, using code from the answer above:
def Emailer(message, subject, recipient):
import win32com.client as win32
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = recipient
mail.Subject = subject
mail.GetInspector
index = mail.HTMLbody.find('>', mail.HTMLbody.find('<body'))
mail.HTMLbody = mail.HTMLbody[:index + 1] + message + mail.HTMLbody[index + 1:]
mail.Display(True)
#mail.send #uncomment if you want to send instead of displaying
then call
Emailer("Hi there, how are you?", "Subject", "david#bisnode.com")
Outlook signatures are not exposed through the Outlook Object Model. The best you can do is read the signature from the file system and add its contents to the HTML body appropriately. Keep in mind that two HTML strings must be merged, not just concatenated. You would also need to merge the styles from two HTML documents and take care of the embedded images used by the signature.
Note that Outlook adds a signature when an unmodified message is displayed or its inspector is touched
import win32com.client as win32
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = 'TO'
mail.Subject = 'SUBJECT'
mail.GetInspector
mail.HTMLBody now contains the message signature that you will need to merger (not just concatenate!) with your own HTML
UPDATE: as of the latest (Summer 2016) builds of Outlook, GetInspector trick no longer works. Now Only MailItem.Display adds the signature to an unmodified message.
If you want to programmatically insert a signature, Redemption (I am its author) exposes RDOSignature object which implements ApplyTo method (it handles the signature image files and merges HTML styles appropriately).
You can find the signature in Outlook stored as an HTML file in %APPDATA%\Microsoft\Signatures and I used the following code to copy the file contents and add them to my email body
import win32com.client
import os
signature_path = os.path.join((os.environ['USERPROFILE']),'AppData\Roaming\Microsoft\Signatures\Work_files\\') # Finds the path to Outlook signature files with signature name "Work"
html_doc = os.path.join((os.environ['USERPROFILE']),'AppData\Roaming\Microsoft\Signatures\Work.htm') #Specifies the name of the HTML version of the stored signature
html_doc = html_doc.replace('\\\\', '\\') #Removes escape backslashes from path string
html_file = codecs.open(html_doc, 'r', 'utf-8', errors='ignore') #Opens HTML file and ignores errors
signature_code = html_file.read() #Writes contents of HTML signature file to a string
signature_code = signature_code.replace('Work_files/', signature_path) #Replaces local directory with full directory path
html_file.close()
olMailItem = 0x0
outlook = win32com.client.Dispatch("Outlook.Application")
newMail = outlook.CreateItem(olMailItem)
newMail.CC = "my#email.com"
newMail.Subject = subject
newMail.BodyFormat = 2 # olFormatHTML https://msdn.microsoft.com/en-us/library/office/aa219371(v=office.11).aspx
newMail.HTMLBody = "Email Message" + signature_code
newMail.display()
It seems I needed to replace the local path to the Signature files, with the absolute path in order to use images,etc.
sig_files_path = 'AppData\Roaming\Microsoft\Signatures\\' + signature_name + '_files\\'
sig_html_path = 'AppData\Roaming\Microsoft\Signatures\\' + signature_name + '.htm'
signature_path = os.path.join((os.environ['USERPROFILE']), sig_files_path) # Finds the path to Outlook signature files with signature name "Work"
html_doc = os.path.join((os.environ['USERPROFILE']),sig_html_path) #Specifies the name of the HTML version of the stored signature
html_doc = html_doc.replace('\\\\', '\\')
html_file = codecs.open(html_doc, 'r', 'utf-8', errors='ignore') #Opens HTML file and converts to UTF-8, ignoring errors
signature_code = html_file.read() #Writes contents of HTML signature file to a string
signature_code = signature_code.replace((signature_name + '_files/'), signature_path) #Replaces local directory with full directory path
html_file.close()
you should be able to do this if your signature is set as default.
>>> signature = message.body
>>> message.body = "ahoj\n" + signature
You acan also use message.HTMLbody if your signature contains picture.
Your signature should always apear in the message if you set it as default. You will save current content of the body to signature variable and then add it to the end of the message. Works for me at least.
I started to apply the code that the good samaritan linktotherescue posted us above.
After doing research on the inspector feature I could make it by doing another signature at Outlook and changing the current image to a text called {IMAGE} then with "Find" I used to search the text and replaced with the image from my original signature.
import win32com.client as win32
import os
import codecs
sig_files_path "C://Users//RenterSa//AppData//Roaming//Microsoft//Signatures//Technical Support Engineer_archivos"
sig_html_path = "C://Users//RenterSa//AppData//Roaming//Microsoft//Signatures//TSE (Python).htm"
img_path = r'C:\Users\RenterSa\AppData\Roaming\Microsoft\Signatures\Technical Support Engineer_archivos\image001.jpg'
signature_path = os.path.join((os.environ['USERPROFILE']), sig_files_path) # Finds the path to Outlook signature files with signature name "Work"
html_doc = os.path.join((os.environ['USERPROFILE']),sig_html_path) #Specifies the name of the HTML version of the stored signature
html_doc = html_doc.replace('\\\\', '\\')
html_file = codecs.open(html_doc, 'r', 'utf-8', errors='ignore') #Opens HTML file and converts to UTF-8, ignoring errors
signature_code = html_file.read() #Writes contents of HTML signature file to a string
signature_code = signature_code.replace((sig_html_path + sig_files_path),
signature_path) #Replaces local directory with full directory path
html_file.close()
olMailItem = 0x0
outlook = win32.Dispatch("Outlook.Application")
newMail = outlook.CreateItem(olMailItem)
newMail.CC = "my#email.com"
newMail.Subject = "subject"
newMail.Importance = 2
newMail.BodyFormat = 3 # olFormatHTML https://msdn.microsoft.com/en-us/library/office/aa219371(v=office.11).aspx
newMail.HTMLBody = "Email Message" + signature_code
inspector = newMail.GetInspector
newMail.display()
doc = inspector.WordEditor
selection = doc.Content
selection.Find.Text = r"{IMAGE}"
selection.Find.Execute()
selection.Text = ""
img = selection.InlineShapes.AddPicture(img_path, 0 , 1)