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

sireesha
  • 135
  • 1
  • 2
  • 11
  • 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() ` – Ben.T Apr 10 '18 at 20:04
  • 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. – sireesha Apr 11 '18 at 07:36
  • If you don't restart your python script, I'm not sure how to do it. If you do, then see my answer – Ben.T Apr 11 '18 at 15:11
  • Thanks a lot Ben !!!... – sireesha Apr 12 '18 at 06:28
  • 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? – Ben.T Apr 12 '18 at 12:50

2 Answers2

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.

Ben.T
  • 29,160
  • 6
  • 32
  • 54
  • 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 mails – sireesha Apr 12 '18 at 11:20
  • Thanks Ben even i noticed the same when i tried yesterday... will check the code and let you know – sireesha Apr 13 '18 at 10:11
  • Hi Ben, is there a way to start/stop pythoncom.PumpMessages() at regular intervals lets say at every one hour – sireesha Apr 17 '18 at 14:29
  • 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) – sireesha Apr 18 '18 at 06:54
  • @Ben.T 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! – AiRiFiEd Mar 11 '19 at 08:40
  • @Ben.T 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! – AiRiFiEd Mar 12 '19 at 00:05
  • To elaborate, for example, `DispatchWithEvents` somehow knows that `Handler_Class` has the method `OnNewMailex`(which I believe comes from https://learn.microsoft.com/en-us/office/vba/api/outlook.application.newmailex) 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! – AiRiFiEd Mar 12 '19 at 00:11
  • @Ben.T 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) – AiRiFiEd Mar 12 '19 at 03:07
  • @AiRiFiEd 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 ;) – Ben.T Mar 12 '19 at 13:44
  • @Ben.T apologies Ben my questions typically get down-posted...no choice but to ask through comments - apologies! – AiRiFiEd Mar 12 '19 at 15:47
  • I am trying to change the subect but its not working. Can anyone tell me to work on partial subject texts? – guialmachado Apr 29 '20 at 03:35
  • 1
    @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. – yuk Feb 23 '21 at 16:29
  • @yuk nice use of this code pushing notification on the phone.. I`m sure that the VBA version can be coded, good luck with that :) – Ben.T Feb 24 '21 at 14:22
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()
RobC
  • 22,977
  • 20
  • 73
  • 80