Python get only the most recent Outlook email from a conversation - python

I'm using Python 3.5 and iterating through Outlook emails searching by message subject, if a condition is met I Save the mail to desktop. I have a problem because as I iterate through mails, I end up getting all the mails in a conversation (Both original mail and all the responses RE:) when i print the subjects that met the if condition.
On the other hand, when i save them to desktop (message.SaveAs) i get only the first email in a conversation.
What i'm interested in is only the most recent mail from a conversation, because if I save that to desktop i also get all the previous responses, so no need for 30 .msg files. Is there a way to do it? Here's my code:
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
folder = outlook.Folders("Main")
subfolder = folder.Folders("Incoming")
inbox = subfolder.Folders("folder1")
for x in IDX:
messages = inbox.Items
message = messages.GetFirst()
for _ in itertools.repeat(None, 100):
try:
Subject = message.subject
if x in Subject:
print(Subject)
message.SaveAs(desktop + '\\' + Subject + ".msg", OlSaveAsType['olMSG'])
message = messages.GetNext()
except:
message = messages.GetNext()

Firstly, do not loop through all items in a folder - call Items.Restrict() passing a restriction like " [Subject] = 'you subject' ", then sort the returned Items collection (Items.Sort) on ReceivedTime.

Related

Forward emails from a specifc sender in outlook via python

i try the below code but the attribute error is driving me crazy !, any thing after message. gets me an attribute error, wtv prop. i use. i end up with attribute error.
AttributeError: <unknown>.Sender
Code:
import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6) # "6" refers to the inbox
messages = inbox.Items
sender_email = "TDC#AE.Roco.COM"
recipient_email = "simple.invoice#net"
for message in messages:
if message.Sender.Address == sender_email:
new_mail = message.Forward()
new_mail.Recipients.Add(recipient_email)
for attachment in message.Attachments:
new_mail.Attachments.Add(attachment)
new_mail.Save()
Based on given answers:
import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application")
mapi = outlook.GetNamespace("MAPI")
inbox = mapi.GetDefaultFolder(6)
accounts = mapi.Folders
query = '#SQL="urn:schemas:httpmail:from" = ' + "'TDC#AE.Roco.COM'" + ' AND "urn:schemas:httpmail:hasattachment" = ' + "'1'"
print(query)
try:
items = inbox.Items.Restrict(query)
print(f'Number of items found : {items.count}')
def check_subfolders(folder):
items = folder.Items.Restrict(query)
if items.count > 0:
print(f'{items.count} emails found in {folder.name}')
for subfolder in folder.Folders:
check_subfolders(subfolder)
check_subfolders(inbox)
for folder in mapi.Folders:
items = folder.Items.Restrict(query)
if items.count > 0:
print(f'{items.count} emails found in {folder.name}')
for item in items:
mail = item.Forward()
mail.Recipients.Add("simple.invoice#net")
mail.Subject = "Fwd: " + item.Subject
mail.Body = "Please find the forwarded message with attachments below:\n\n" + item.Body
mail.Save()
except Exception as e:
print(f'An error occurred: {e}')
Now I have no errors but the result returns zero, although I have mails from that specified sender!
Quick Example
import win32com.client
def outlook_emails(Inbox):
Filter_sender = "#SQL=""urn:schemas:httpmail:fromemail"" " \
"ci_phrasematch 'TDC#AE.Roco.COM'"
items = Inbox.Items.Restrict(Filter_sender)
print(items.Count)
for i in reversed(range(items.Count, 0, -1)):
if items[i].Class == 43:
print(items[i].Subject)
if __name__ == "__main__":
outlook = win32com.client.Dispatch(
"outlook.Application").GetNamespace(
"MAPI"
)
inbox = outlook.GetDefaultFolder(6)
outlook_emails(inbox)
#SQL=""urn:schemas:httpmail:fromemail"" ci_phrasematch
Not all Outlook items provide the Sender property. Outlook folders may contains different kind of items - notes, calendar items, mail items, document items and etc. So, it makes sense to make sure that you deal with a mail item, for example, you could check the MessageClass property.
Instead of iterating over all items in Outlook:
for message in messages:
if message.Sender.Address == sender_email:
Use the Find/FindNext or Restrict methods of the Items class. They allow getting items that correspond to the search criteria, so you can iterate over them without checking a property each time. Read more about these methods in the articles that I wrote for the technical blog:
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
Use the following search criteria to get items from the specified email address:
criteria = "#SQL=" & Chr(34) _
& "urn:schemas:httpmail:senderemail" & Chr(34) _
& " = 'eugene#astafiev.com'"
If you need to send an item from a specific sender, you can use the MailItem.SendUsingAccount property returns or sets an Account object that represents the account under which the MailItem is to be sent. Note, the other account should be configured in Outlook. For example, a sample in VBA shows how to use it:
Sub SendUsingAccount()
Dim oAccount As Outlook.account
For Each oAccount In Application.Session.Accounts
If oAccount.AccountType = olPop3 Then
Dim oMail As Outlook.MailItem
Set oMail = Application.CreateItem(olMailItem)
oMail.Subject = "Sent using POP3 Account"
oMail.Recipients.Add ("someone#example.com")
oMail.Recipients.ResolveAll
Set oMail.SendUsingAccount = oAccount
oMail.Send
End If
Next
End Sub
Another approach is to use the MailItem.SentOnBehalfOfName property which returns or sets a string indicating the display name for the intended sender of the mail message. In that case the other user should have permissions to send on behalf of another person in Exchange.
Sender property is only exposed by the MailItem object, but you can also have ReportItem and MeetingItem objects in the Inbox folder. You need to check first that Class property == 43 (which is olMail)
Also, do not loop through all items in a folder - use Items.Find/FindNext or Items.Restrict with a query like [SenderEmailAddress] = 'TDC#AE.Roco.COM'

how to use win32com.mapi get new email from outlook in python?

I wanna get a message when a new email into outlook.
I try to use python.
I can find that folders,but can't find the way to get the number of new email.
import win32com.client
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
accounts = win32com.client.Dispatch("Outlook.Application").Session.Accounts;
inbox = outlook.Folders(accounts[0].DeliveryStore.DisplayName)
for obj in inbox.Folders: #how to know how many new email in this dir?
try:
if hasattr(obj, "__str__"):
dirName = obj.__str__() #as some new email in this obj.
for message in obj.items: # how to know this email is not be read ?
subject = sender = ''
if hasattr(message, "Subject"):
subject = message.Subject
if hasattr(message, "SenderName"):
sender = message.SenderName
print(sender, subject)
except Exception as e:
print(f"i:{obj.__str__()}")
and where can I learn win32com.mapi?
I wanna know what func can I use in mapi.
give me some info,please~
so much thanks!
Are you looking for unread messages? Use MAPIFolder.Items.Restrict("[Unread] = true") to retrieve Items collection with the restriction applied.

save outlook attachments with python

Every Monday I receive the attachments with the slightly changed subject title. Constant part of the subject title is PB Report and will have the Monday's date. For example I received email this Monday with subject PB Report - 13.12.2021, last week PB Report - 06.12.2021 and so on. I would like to implement the GetLast in this code in order to get only the newest sent report. Also, how do I tell python to search the subject which starts with PB Report and dont look at the rest of the title. I tried wildcard(*) as save_attachments('PB Report*') but did not work.
import datetime
import os
import win32com.client
path = r"C:/Users/greencolor/Desktop/Autoreport/"
today = datetime.date.today()
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
def save_attachments(subject):
for message in messages:
if message.Subject == subject:
for attachment in message.Attachments:
print(attachment.FileName)
attachment.SaveAsFile(os.path.join(path, str(attachment)))
if __name__ == "__main__":
save_attachments('PB Report - 13.12.2021')
I also have the alternative code but when i run this code i never get the result nor error. It takes.
import os
import win32com.client
path = r"C:/Users/greencolor/Desktop/Autoreport/"
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
message = messages.GetLast()
while message:
if 'PB' in message.subject and 'Report' in message.subject:
for attachment in message.Attachments:
print(attachment.FileName)
attachment.SaveAsFile(os.path.join(path, str(attachment)))
The wildcard for parameter subject wont work because parameter subject is used as string when comparing for equality in message.Subject == subject.
You could instead use string-method startswith on the message subject like message.Subject.startswith(subject_prefix)
and call your method with a common prefix like save_attachments('PB Report - ').
Furthermore use the attachment.FileName to construct the output-file path.
You could use GetLast() or Sort() with appropriate message property to filter on the newest sent report. Or you could parse the dates in message's subject and sort them accordingly. However this would deserve another question, own research and further specification and focus.
Solution
An example solution could be as follows (see comments for adjustments):
def save_attachments(subject_prefix): # changed parameter name
messages.Sort("[ReceivedTime]", True) # sort by received date: newest to oldest
for message in messages:
if message.Subject.startswith(subject_prefix): # changed test
print("saving attachments for:", message.Subject)
for attachment in message.Attachments:
print(attachment.FileName)
attachment.SaveAsFile(os.path.join(path, str(attachment.FileName))) # changed to file-name
return # exit after first matched message
Alternatively use GetLast() if you know the messages only contain desired ones, sorted by date.
message = messages.GetLast()
while message: # to test if there is a (last) message at all
# save attachment
Related questions
str.startswith with a list of strings to test for
Search the entire Outlook email box for specific emails using Python
How to go through Outlook emails in reverse order using Python

Python Outlook headers

I got a script to extract Gmailheaders. There is a lot of information to extract.
When I try to extract data from outlook it feels like im getting a small part.
The script I currently got is:
import win32com.client
import re
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
message = messages.GetFirst()
rec_time = message.CreationTime
body_content = message.body
while message:
To = message.To
Recipients = message.Recipients
Sender = message.Sender
address = message.Sender.Address
cc = message.CC
Importance = message.Importance
LastModificationTime = message.LastModificationTime
It prints the following fields:
print message.subject
print message.CreationTime
print To
print Sender
print address
print cc
print Importance
print LastModificationTime
Is there just a limit amount of Outlook headers you can extract or?
I've tryed to look on sources like:
https://msdn.microsoft.com/en-us/VBA/Outlook-VBA/articles/mailitem-object-outlook
I am missing important information like sender IP-adresses. Is there anyway of extracting more information without using a 3partytool?
Thanks!
Edit:
Works:
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
messages = inbox.Items
for message in messages:
msg = message.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001F")
To see all MIME headers, read the PR_TRANSPORT_MESSAGE_HEADERS MAPI property (DASL name http://schemas.microsoft.com/mapi/proptag/0x007D001F) using MailItem.PropertyAccessor.GetProperty.
You can see that property (as well as all other MAPI and OOM properties) using OutlookSpy (I am its author) - click IMessage button.

Extract sender's email address from Outlook Exchange in Python using win32

I am trying to extract the sender's email address from outlook 2013 using win32 package in python. There are two kinds of email address type in my Inbox, exchange and smtp. If I try to print the the sender's email address of Exchange type, I am getting this:
/O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP(FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=6F467C825619482293F429C0BDE6F1DB-
I have already gone through this link but couldn't find a function through which I can extract the smtp address.
Below is my code:
from win32com.client import Dispatch
outlook = Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder("6")
all_inbox = inbox.Items
folders = inbox.Folders
for msg in all_inbox:
print msg.SenderEmailAddress
Currently all the Email Address are coming like this:
/O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP(FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=6F467C825619482293F429C0BDE6F1DB-
I found a solution to this in VB.net link but don't know how to rewrite the same thing in Python.
Firstly, your code will fail if you have an item other than MailItem in the folder, such as ReportItem, MeetingItem, etc. You need to check that the Class property is 43 (olMail).
Secondly, you need to check the sender email address type and use the SenderEmailAddress property only for the "SMTP" address type. In VB:
for each msg in all_inbox
if msg.Class = 43 Then
if msg.SenderEmailType = "EX" Then
print msg.Sender.GetExchangeUser().PrimarySmtpAddress
Else
print msg.SenderEmailAddress
End If
End If
next
I am just modifying the program given above in Python.
from win32com.client import Dispatch
outlook = Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder("6")
all_inbox = inbox.Items
folders = inbox.Folders
for msg in all_inbox:
if msg.Class==43:
if msg.SenderEmailType=='EX':
print msg.Sender.GetExchangeUser().PrimarySmtpAddress
else:
print msg.SenderEmailAddress
This will print out all the sender's email address in your inbox folders only.
I had this same problem workin with win32com today. I found the solution here.
Using your example it would be:
from win32com.client import Dispatch
outlook = Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder("6")
all_inbox = inbox.Items
folders = inbox.Folders
for msg in all_inbox:
if msg.Class==43:
if msg.SenderEmailType=='EX':
if msg.Sender.GetExchangeUser() != None:
print msg.Sender.GetExchangeUser().PrimarySmtpAddress
else:
print msg.Sender.GetExchangeDistributionList().PrimarySmtpAddress
else:
print msg.SenderEmailAddress
This should solve the group mail issue.

Categories