我想在Python中制作一个Outlook监视器,并一直在研究这个主题。
我正在使用Python 3.6尝试监控我的Outlook上共享收件箱中收到的电子邮件。我有以下代码将运行:
import win32com.client
import ctypes # for the VM_QUIT to stop PumpMessage()
import pythoncom
import re
import time
import psutil
import os
from os.path import join as join_path
from olefile import OleFileIO
class HandlerClass():
def __init__(self):
# First action to do when using the class in the DispatchWithEvents
messages = self # Getting All Messages in the Inbox Folder
#headers_schema = "http://schemas.microsoft.com/mapi/proptag/0x007D001E"
# Check for unread emails when starting the event
for message in messages:
if message.UnRead:
self.check_email(message)
else:
print("No Unread Emails")
break
def check_email(self, message):
attachments = message.Attachments # Get attachments
if attachments.Count > 0: # If one or more attachments
attachment = attachments.Item(1) # Get the first attachment
attachment_fname = join_path(os.getcwd(), attachment.FileName)
attachment.SaveAsFile(attachment_fname) # Saves to the attachment to current folded
try:
IHDict = extract_headers(attachment_fname)
except MessageHeadersError as err:
print("Could not extract headers. Is this an email?", err)
return
finally:
os.remove(attachment_fname) # Delete saved attachment
def check_header(h: str) -> bool:
hval = IHDict.get(h, None)
if hval is not None:
print("%s: %s" % (h, hval))
else:
print("'%s' not available." % h)
return hval
# Pull out headers of interest
rp = check_header('Return-Path')
sp = check_header('X-Env-Sender')
check_header('X-Originating-Ip')
check_header('Authentication-Results')
print() # Formatting
# Compare Return Path to X-Env-Sender to check for Phish.
if rp is not None and sp is not None and rp.strip("<>") == sp:
print("Email is safe.")
print() # Formatitng
else:
print("Email is suspicious.")
print() # Formatting
else:
print("Email Contains No Attachment Message")
def OnQuit(self):
# To stop PumpMessages() when Outlook Quit
# Note: Not sure it works when disconnecting!!
ctypes.windll.user32.PostQuitMessage(0)
def OnItemAdd(self, mail):
print("Working!")
#self.check_email(mail)
#Check if the item is of the MailItem type
if mail.Class==43:
print(mail.Subject, " - ", mail.Parent.FolderPath)
# Function to check if outlook is open
def check_outlook_open():
for pid in psutil.pids():
p = psutil.Process(pid)
if p.name().lower().strip() == 'outlook.exe':
return True
return False
class MessageHeadersError(Exception):
pass
class MessageHeadersMissing(MessageHeadersError):
pass
class MessageHeadersUnknownEncoding(MessageHeadersError):
pass
def extract_headers(cdfv2_filename: str) -> dict:
"""Extract headers from a CDFv2 email"""
try:
ole = OleFileIO(cdfv2_filename)
except Exception as exc:
raise MessageHeadersError("could not open OLE file") from exc
try:
for ent in ole.listdir(streams=True, storages=False):
if ent[-1].startswith("__substg1.0_007D"):
# message headers
break
else:
# no message header entry?
raise MessageHeadersMissing("missing")
olestream = ole.openstream(ent)
header_data = olestream.read()
olestream.close()
if ent[-1].endswith("001E"):
# ASCII encoding
header_string = header_data.decode("ascii")
elif ent[-1].endswith("001F"):
# UTF-16
header_string = header_data.decode("UTF-16")
else:
# dunno what this encoding is
raise MessageHeadersUnknownEncoding("Unknown OLE encoding " + ent[-4:])
return parse_headers(header_string)
finally:
ole.close()
def parse_headers(hstr: str) -> dict:
headers = {}
lastkey = None
for line in hstr.split("\n"):
#if line.strip() == " ": #skip empty lines - there shouldn't be any. continue
if line.startswith("\t") or line.startswith(" "): #headers can be continued with either whitespace or tabs.
key = lastkey
value = line.strip()
else:
key, _, value = line.partition(":")# e - mail headers are case insensitive, #so we normalise to title - case (the most common form).
key = key.strip().title()
value = value.strip()
lastkey = headers[key] = (headers.get(key, "") + " " + value).strip()
return headers
# Loop
Syscoms = win32com.client.DispatchEx("Outlook.Application").GetNamespace("MAPI").Folders["UK"].Folders["Inbox"].Items
while True:
# If outlook opened then it will start the DispatchWithEvents
if check_outlook_open():
win32com.client.DispatchWithEvents(Syscoms, HandlerClass)
pythoncom.PumpMessages()
# To not check all the time (should increase 10 depending on your needs)
time.sleep(1)
如何在新邮件到达收件箱时触发OnItemAdd事件?
答案 0 :(得分:0)
尝试使用Dispatch
代替DispatchEx
,并使用GetSharedDefaultFolder
代替Inbox
。以下代码将收到的电子邮件正确注册到共享收件箱:
import ctypes # for the VM_QUIT to stop PumpMessage()
import pythoncom
import win32com.client
import sys
# outlook config
SHARED_MAILBOX = "Your Mailbox Name"
# get the outlook instance and inbox folder
session = win32com.client.Dispatch("Outlook.Application").Session
user = session.CreateRecipient(SHARED_MAILBOX)
shared_inbox = session.GetSharedDefaultFolder(user, 6).Items # 6 is Inbox
class HandlerClass(object):
def OnItemAdd(self, item):
print("New item added in shared mailbox")
if item.Class == 43:
print("The item is an email!")
outlook = win32com.client.DispatchWithEvents(shared_inbox, HandlerClass)
def main():
print("Starting up Outlook watcher")
pythoncom.PumpMessages()
if __name__ == "__main__":
try:
status = main()
sys.exit(status)
except KeyboardInterrupt:
print("Terminating program..")
ctypes.windll.user32.PostQuitMessage(0)
sys.exit()
上面的代码中有一个错误,尽管这不是问题的一部分,但我不想这样。程序不会像这样捕获KeyboardInterrupt
,因为PumpMessages
是一个无限循环,会阻塞主线程。使用threading
模块将其放入自己的线程中可以解决此问题:
class OtlkThread(threading.Thread):
def run(self):
logger.info("Starting up Outlook watcher\n"
"To terminate the program, press 'Ctrl + C'")
pythoncom.PumpMessages()
def main():
win32com.client.DispatchWithEvents(shared_inbox, HandlerClass)
OtlkThread(daemon=True).start()
if __name__ == "__main__":
status = main()
while True:
try:
time.sleep(1)
except KeyboardInterrupt:
logger.info("Terminating program..")
ctypes.windll.user32.PostQuitMessage(0)
sys.exit(status)