-1

Below is the code to download attachments from a sender with a specific subject. I need to deploy it locally on my computer so that this code keeps monitoring outlook and as soon as the email arrives, it should download the attachment. I have earlier deployed ml models with flask. I need help to deploy this locally. Currently running the .py script in batch mode

import imaplib
import base64
import os
import email
import imap_tools
from imap_tools import MailBox
import win32com.client
import os
from datetime import datetime, timedelta
outlook = win32com.client.Dispatch('outlook.application')
mapi = outlook.GetNamespace("MAPI")
messages = inbox.Items
received_dt = datetime.now() - timedelta(days=7)
received_dt = received_dt.strftime('%m/%d/%Y %H:%M %p')
messages = messages.Restrict("[ReceivedTime] >= '" + received_dt + "'")
messages = messages.Restrict("[SenderEmailAddress] = 'XXXX@gmail.com'")
messages = messages.Restrict("[Subject] = '[EXTERNAL] Fwd: Expense Report'")
outputDir = r"\\BLRESGANALXXXX\XXXXX\attachment_download"
try:
    for message in list(messages):
        try:
            s = message.sender
            for attachment in message.Attachments:
                attachment.SaveASFile(os.path.join(outputDir,attachment.FileName))
                print(f"attachment {attachment.FileName} from {s} saved")
        except Exception as e:
            print("error when saving the attachment:" + str(e))
except Exception as e:
    print("error when processing emails messages:" + str(e))
davidism
  • 121,510
  • 29
  • 395
  • 339
  • I don't really get what you're asking. Do you need help running this every few minutes? Or does this not work at all? – Ofer Sadan Aug 26 '22 at 07:17
  • @OferSadan this program works. I need this deployed running on localhost so that it runs constantly looking for emails that satisfy these conditions. Basically I do not want to run this program manually. – Subhojyoti Lahiri Aug 26 '22 at 07:27
  • You can run the script periodically with a crontab (https://stackoverflow.com/questions/8727935/execute-python-script-via-crontab)....depending on your frequency, it may not be immediately when the email arrives. – evanstjabadi Aug 26 '22 at 07:48
  • I get the idea of using a batch mode to execute it. I want to get past the dependency of cron and try to deploy it locally. – Subhojyoti Lahiri Aug 26 '22 at 12:23

1 Answers1

2

One solution is to register to receive certain Events from the Outlook application. This code hooks into the OnItemAdd Event of the Inbox's Items collection.

import win32com.client
import pythoncom

#Class to handle events from an Items collection object
class Items_Handler(object):
    def OnItemAdd(self,item): #Handles Items::OnItemAdd event
        #Test if it is a MailItem
        if item.Class == win32com.client.constants.olMail:
            print('New Mail Item!')
            print('   From:',item.SenderEmailAddress)
            print('   Subject:',item.Subject)
            print('   Received at:',item.ReceivedTime)
            #Do whatever you want with the message (eg save attachments)

#Create the Outlook application object
outlook = win32com.client.gencache.EnsureDispatch('Outlook.Application')
#Get the Inbox
inBox = outlook.GetNamespace('MAPI').Folders['your.email@address.com'].Folders['Inbox']
#Get the Items collection for the Inbox
items = inBox.Items
#Tell Outlook you are interested in the events for the Items collection
win32com.client.WithEvents(items,Items_Handler)

print('Listening ...')
#Message loop: will handle incoming events/messages until script killed
pythoncom.PumpMessages()

You can amend the OnItemAdd handler to do whatever you want with the message, eg filter for sender, subject, save attachments etc.

NB. It will only process new messages, so you may still need your original code to sweep up any new messages in your InBox that arrived before this script started.

There is also a caveat in the documentation for ItemAdd that the event might not fire if 'a large number' of items are added to the folder at once.

DS_London
  • 3,644
  • 1
  • 7
  • 24
  • I have tried to filter by statement if item.SenderEmailAddress=='xxx@gmail.com' and item.Subject=='[EXTERNAL] Fwd: Expense Report' and used for attachment in item.Attachments: to download attachments with attachment.SaveASFile but it says AttributeError: '' object has no attribute 'SaveASFile' – Subhojyoti Lahiri Aug 28 '22 at 22:14
  • @SubhojyotiLahiri That is because the Attachment object does not have a method called `SaveASFile`. Fortunately it does have one called `SaveAsFile`! One "gotcha" side effect of choosing `win32com.client.gencache.EnsureDispatch()` to create the object is that method and property names are case-sensitive and you have to get them exactly right. https://learn.microsoft.com/en-us/office/vba/api/outlook.attachment.saveasfile – DS_London Aug 29 '22 at 06:55
  • Thanks, can't believe I missed that. It definitely works for all emails received. – Subhojyoti Lahiri Aug 29 '22 at 07:24