How to continuously monitor a new mail in outlook and unread mails of a specific folder in python
Asked Answered
P

2

8

I want to check for a particular sender email and process it automatically wherever it arrives

However, there may be some situation where my outlook was restarted, mean while i received mail from sender and marked as unread

For continuous monitor for a new mail for a specific subject i have found the following code

import win32com.client
import pythoncom
import re

class Handler_Class(object):
  def OnNewMailEx(self, receivedItemsIDs):
    # RecrivedItemIDs is a collection of mail IDs separated by a ",".
    # You know, sometimes more than 1 mail is received at the same moment.
    for ID in receivedItemsIDs.split(","):
        mail = outlook.Session.GetItemFromID(ID)
        subject = mail.Subject
    print subject   
        try: 
            command = re.search(r"%(.*?)%", subject).group(1)

            print command # Or whatever code you wish to execute.
        except:
            pass


outlook = win32com.client.DispatchWithEvents("Outlook.Application",Handler_Class)

#and then an infinit loop that waits from events.
pythoncom.PumpMessages() 

Even i want to go through all the unread mails to check whether a mail from a sender has came and process it( if found)

Is there any function to check for unread mails to add within handler_class

Or let me know for any alternate procedure

Philemon answered 6/4, 2018 at 14:32 Comment(5)
do you plan to restart you python program after your outlook is restarted? if yes you can check for unread email as first action of your script before doing pythoncom.PumpMessages() Corporation
I am looking for a case if my outlook credentials got expired and tried to login with my new credentials or my system was restarted .. mean while i got 3 mails ... can you suggest me how to proceed with these 2 cases . .also can you put some sample code for the above comment.Philemon
If you don't restart your python script, I'm not sure how to do it. If you do, then see my answerCorporation
Thanks a lot Ben !!!...Philemon
I have a question, did you try your code once you restart Outlook? by this I mean, do you still get monitoring on received message (I'm not taking about unread one) if you don't restart your Python script after restarting your Outlook?Corporation
C
16

So if you restart your python script every time your Outlook restart, then add these lines to your code to check unread emails in your Inbox:

ol = win32com.client.Dispatch( "Outlook.Application")
inbox = ol.GetNamespace("MAPI").GetDefaultFolder(6)
for message in inbox.Items:
    if message.UnRead == True:
        print message.Subject #or whatever command you want to do

Put this code before your definition of outlook in your code

EDIT

For me, the code you posted works great until I close Outlook and then even if I reopen it, I don't get anything when a new message is received (see one of my comments). I guess the fact of closing Outlook "unlink" with pythoncom.PumpMessages(). Anyway, I come around to do both your checking for unread email in the class Handler_Class and restart the monitoring in case you restart Outlook.

import win32com.client
import ctypes # for the VM_QUIT to stop PumpMessage()
import pythoncom
import re
import time
import psutil

class Handler_Class(object):

    def __init__(self):
        # First action to do when using the class in the DispatchWithEvents     
        inbox = self.Application.GetNamespace("MAPI").GetDefaultFolder(6)
        messages = inbox.Items
        # Check for unread emails when starting the event
        for message in messages:
            if message.UnRead:
                print message.Subject # Or whatever code you wish to execute.

    def OnQuit(self):
        # To stop PumpMessages() when Outlook Quit
        # Note: Not sure it works when disconnecting!!
        ctypes.windll.user32.PostQuitMessage(0)

    def OnNewMailEx(self, receivedItemsIDs):
    # RecrivedItemIDs is a collection of mail IDs separated by a ",".
    # You know, sometimes more than 1 mail is received at the same moment.
        for ID in receivedItemsIDs.split(","):
            mail = self.Session.GetItemFromID(ID)
            subject = mail.Subject
            print subject   
            try: 
                command = re.search(r"%(.*?)%", subject).group(1)
                print command # Or whatever code you wish to execute.
            except:
                pass

# Function to check if outlook is open
def check_outlook_open ():
    list_process = []
    for pid in psutil.pids():
        p = psutil.Process(pid)
        # Append to the list of process
        list_process.append(p.name())
    # If outlook open then return True
    if 'OUTLOOK.EXE' in list_process:
        return True
    else:
        return False

# Loop 
while True:
    try:
        outlook_open = check_outlook_open()
    except: 
        outlook_open = False
    # If outlook opened then it will start the DispatchWithEvents
    if outlook_open == True:
        outlook = win32com.client.DispatchWithEvents("Outlook.Application", Handler_Class)
        pythoncom.PumpMessages()
    # To not check all the time (should increase 10 depending on your needs)
    time.sleep(10)

Not sure it is the best way, but it seems to work the way you look for.

Corporation answered 11/4, 2018 at 15:11 Comment(13)
Anyone else how can you suggest me how to proceed with this case, if my outlook credentials got expired and tried to login with my new credentials .. mean while i got 3 mails ... how to processed to check to these mailsPhilemon
Thanks Ben even i noticed the same when i tried yesterday... will check the code and let you knowPhilemon
Hi Ben, is there a way to start/stop pythoncom.PumpMessages() at regular intervals lets say at every one hourPhilemon
Hi Ben, i tried something like this in my code if outlook_open == True: outlook=win32com.client.DispatchWithEvents("Outlook.Application", Handler_Class) while True: start_time= while time.clock() < 100: pythoncom.PumpWaitingMessages() outlook_open=processExists('OUTLOOK.EXE') if outlook_open == False: ctypes.windll.user32.PostQuitMessage(0) break # To not check all the time (should increase 10 depending on your needs) if outlook_open == False: print "outlook not opened" os.startfile("outlook") time.sleep(10)Philemon
@Corporation Hi Ben, can i check with you if there's any way to monitor only 1 mailbox within OnNewMailEx? I have been stuck at this for awhile and was wondering if it is either possible to DispatchWithEvents a particular mailbox/folder or test if mailitem is from a particular account? I cant seem to find such a property from MSDN. Thank you!Pisolite
@Corporation No worries Ben and thanks for your help! Have tried Dispatch methods and would very much prefer an event-driven approach but am having a hard time with DispatchWithEvents given the lack of documentations on win32com. Problem with To and Recipients properties is that most mails are sent to both mailboxes (one being the group mailbox). By any chance would you know what happens behind the scenes when I pass 'Outlook.Application' and Handler_Class to the DispatchWithEvents method? i.e. what arguments can __init__(self,...) in Handler_Class take? Thanks again!Pisolite
To elaborate, for example, DispatchWithEvents somehow knows that Handler_Class has the method OnNewMailex(which I believe comes from learn.microsoft.com/en-us/office/vba/api/…) and somehow passes EntryIDCollection to that method. However, would I be able to write a custom function within Handler_Class that takes arguments? Sorry to trouble!Pisolite
@Corporation something just came to mind - do you thing it would make sense to use try: msg = outlook.Session.GetItemFromID(entry_ID, store_ID and except: pass, where store_ID would be the corresponding store_ID of the folder which I am interested to listen to? This would however require me to first obtain the store_ID of interest, caveat being that store_ID is unique to a machine (from my understanding)Pisolite
@Pisolite sounds a possibility, but you should ask a new question, like this in the comments, it is difficult for me to understand exactly, and you could also get help from more people ;)Corporation
@Corporation apologies Ben my questions typically get down-posted...no choice but to ask through comments - apologies!Pisolite
I am trying to change the subect but its not working. Can anyone tell me to work on partial subject texts?Fraze
@Ben.T: Great code, thanks! I made it to send a push notification to my phone when email arrives from certain senders. Super cool! The only problem I had is if Outlook is closed and opened again the script stops working and even hangs the Outlook. I ended up removing the while True loop to exit the code together with Outlook and then start it manually after Outlook. Also looking for a VBA solution, so I won't have this python window always open.Emotionalism
@Emotionalism nice use of this code pushing notification on the phone.. I`m sure that the VBA version can be coded, good luck with that :)Corporation
C
1

Instead of monitoring outlook from python, try setting up outlook rules for that email and then launch python script via vba.

Here is the code to launch the python script from VBA.

Note: The code below was taken from here.

Sub UruchomBata(MyMail As MailItem)
  Dim Ret_Val
    Dim args As String

    args = "c:\new.py"
    Ret_Val = Shell("C:\python27\python.exe" & " " & args, vbNormalFocus) 
  End Sub

Below this line is the python script for those interested. It is currently set to control the DTR pins on the com1 serial port. The following will need to be save as a yourname.py file

import serial
from time import sleep


conn = serial.Serial('com1',
                     baudrate=9600,
                     bytesize=serial.EIGHTBITS,
                     parity=serial.PARITY_NONE,
                     stopbits=serial.STOPBITS_ONE,
                     timeout=1,
                     xonxoff=0,
                     rtscts=0
                     )
# Wake Modem

conn.setDTR(True)
sleep(3)
conn.setDTR(False)
sleep(1)


conn.close()


# Start talking

try:
    while True:
        conn.write('AT'+chr(13));
        print conn.readline() # readlines() will probably never return.
finally:
    conn.close()
Candor answered 16/8, 2019 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.