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)
Related
Why I cant pass width and height to local images in img tag. Code below is working(image is displaying) but when I add width="100" height="200" to img tag image is not displayed. When I change src to image on the web src="https://i.stack.imgur.com/GafDJ.jpg?s=256&g=1" and add width="100" height="200" image is displayed with added dimensions.
import os
import win32com.client as client
outlook = client.Dispatch('Outlook.Application')
mail = outlook.CreateItem(0)
icon = os.getcwd() + '\\test.jpg'
html_body = f'''
<img src="{icon}">
<!-- <img src="https://i.stack.imgur.com/GafDJ.jpg?s=256&g=1" width="100" height="200"> -->
'''
mail.To = ''
mail.CC = ''
mail.Subject = ''
mail.HTMLBody = html_body
mail.Save()
Local images need to be added as attachments, otherwise the actual image doesn't get sent with the email. The process that has worked for me in the past is to add the image file as an attachment then reference it as a property from the html in the mail.
For example:
import os
import win32com.client as client
outlook = client.gencache.EnsureDispatch('Outlook.Application')
mail = outlook.CreateItem(client.constants.olMailItem)
icon = os.getcwd() + '\\Test.jpg'
#Add a new attachment, putting 0 as third argument means the file is hidden
att = mail.Attachments.Add(icon,client.constants.olByValue,0)
#Create a Content Id (cid) for this image. Can be whatever you want: I'm using short filename
cidName = icon.split('\\')[-1]
att.PropertyAccessor.SetProperty('http://schemas.microsoft.com/mapi/proptag/0x3712001F', cidName);
imgTag = '<img src="cid:{0:}" width="100" height="200">'.format(cidName)
mail.To = ''
mail.CC = ''
mail.Subject = ''
mail.HTMLBody = imgTag
mail.Save()
I am not an expert on how the whole 'schema' thing works, but it seems to do the job. NB. I've used gencache.EnsureDispatch() rather than just Dispatch(). I've found this ensures that the Outlook constants are available (eg olByValue) rather than just using hard-coded numbers.
I'm making a tool for automatic email with Python and win32com.
I would like to attach files in line spacing due to make mail in RTF.
Accomplishment
Hello, world!
{attached file}
Thank you.
Code
outlook = = win32com.client.Dispatch("Outlook.Application")
mail = outlook.createItemFromTemplate(template_item)
mail.to = ""
mail.cc = ""
mail.bcc = ""
mail.bodyformat = 3 # 3: RTF
mail.Attachment.Add(attachment)
mail.Display(True)
With this code, All files were attached to bottom.
I handled with using 'format' method in mail.Body, but the name of file was inserted.
Could you teach me about the approach to attach files to line spacing in email (RTF)?
mail.to = ""
mail.cc = ""
mail.bcc = ""
mail.bodyformat = 3
mail.Display(True) # before attachment
mail.Attachment.Add(Source=attachment,Position=position)
Changing the "Display" layout, I can attach files in line spacing.
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)
Formatted Excel range copied to a Word file
This copies a range of cells from Excel and pastes them into a Word document with formatting preserved. The code works for this. However, I also want to paste the data into the body of an email with the cell styles.
import sys
from pathlib import Path
import win32com.client as win32
excel_path = str(Path.cwd() / 'input.xlsx')
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.Visible = False
excel.DisplayAlerts = False
wb = excel.Workbooks.Open(excel_path)
ws = wb.Worksheets(1)
ws.Range("A1:B2").Copy()
wb.Close()
word_path = str(Path.cwd() / 'output.docx')
word = win32.gencache.EnsureDispatch('Word.Application')
doc = word.Documents.Open(word_path)
doc.Content.PasteExcelTable(False, False, True)
doc.Close()
# That works, but now I want to paste the copied Excel range into the body of
# an email. The solution may look something like the following with the "X"
# as a "Selection" or "Range" object for the PasteExcelTable method.
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
new_mail = outlook.CreateItem(0)
new_mail.To = 'person#email.com'
new_mail.Body = ws.Range("A1:B2").PasteExcelTable(False, False, True)
new_mail.Send()
sys.exit()
I believe [PasteExcelTable]2 can be used to make this happen with Outlook as it does for Word files and perhaps someone knows how this can be done.
I'm not defining the range correctly. Here's an example of one of my attempts:
new_mail.Body = ws.Range("A1:B2").PasteExcelTable(False, False, True)
I know this could be accomplished with VBA, but I want to use Python and to see whether PasteExcelTable will work for this. If it is not possible, I will capture an image of the data range and paste that to the email.
After hours of research and experimentation I found out how to use PasteExcelTable in Outlook. I hope this can be helpful. You only need to have the Excel Table in the clipboard.
import win32com.client
outlook = win32com.client.Dispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = "person#email.com"
mail.Subject = "Subject"
mail.Display()
inspector = outlook.ActiveInspector()
word_editor = inspector.WordEditor
word_range = word_editor.Application.ActiveDocument.Content
word_range.PasteExcelTable(False, False, True)
Hope this will help...
import win32com.client as win32
import pandas as pd
df = pd.read_excel('fullexelpath.xlsx', index_col=False, nrows = 5, usecols = "A:D")
body = df.to_html()
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = 'abc#gmail.com'
mail.HTMLBody = (body)
mail.Send()
This copies a range of cells from Excel and pastes them into a Word document with formatting preserved. The code works for this. However, I also want to paste the data into the body of an email with the cell styles.
There are three main ways for working with bodies in Outlook:
Body - a plain text representing the message body.
HTMLBody.
Word editor. Outlook uses Word as an email editor. The Inspector class provides the WordEditor property which returns an instance of the Document class from the Word object model which represents the message body. Or just use the [GetInspector] method of the MailItem class to retrieve an instance of the Inspector class. You can read more about that in the Chapter 17: Working with Item Bodies .
So, basically you can re-use the same codebase used for Word in case of Outlook.
I was unable to use PasteExcelTable, but I was able to accomplish this with HTMLBody using the following code:
import sys
from pathlib import Path
import win32com.client as win32
from PIL import ImageGrab
excel_path = str(Path.cwd() / 'input.xlsx')
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.Visible = False
excel.DisplayAlerts = False
wb = excel.Workbooks.Open(excel_path)
ws = wb.Worksheets(1)
win32c = win32.constants
ws.Range("A1:B2").CopyPicture(Format=win32c.xlBitmap)
img = ImageGrab.grabclipboard()
image_path = str(Path.cwd() / 'test.png')
img.save(image_path)
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
new_mail = outlook.CreateItem(0)
new_mail.To = 'person#email.com'
new_mail.Attachments.Add(Source=image_path)
body = "<h1>Email text...</h1><br><br> <img src=test.png>"
new_mail.HTMLBody = (body)
new_mail.Send()
wb.Close()
sys.exit()
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.