Python to retrieve Outlook email user profile - python

Is there a way to read the Outlook user's profile information, for example job title, Department, telephone number, and reporting manager, by given an email address?
For example I have 5 email address (internal user in the same company)
I want to quickly find out above information of each email user (probably with win32com?)
Thank you.

You may use the following code.
import win32com.client
import pandas as pd
# Outlook stuff
outApp = win32com.client.gencache.EnsureDispatch("Outlook.Application")
outGAL = outApp.Session.GetGlobalAddressList()
entries = outGAL.AddressEntries
data_set = list()
# Iterates through your contact book and extracts/appends them to a list
for entry in entries:
if entry.Type == "EX":
user = entry.GetExchangeUser()
if user is not None:
if len(user.FirstName) > 0 or len(user.LastName) > 0:
row = list()
row.append(user.FirstName)
row.append(user.LastName)
row.append(user.PrimarySmtpAddress)
row.append(user.Department)
row.append(user.BusinessTelephoneNumber)
row.append(user.MobileTelephoneNumber)
row.append(user.CompanyName)
row.append(user.Name)
row.append(user.JobTitle)
row.append(user.OfficeLocation)
row.append(user.Alias)
row.append(user.City)
row.append(user.Comments)
row.append(user.StateOrProvince)
row.append(user.StreetAddress)
data_set.append(row)
first,second,email,dept,office,mobile,company,outlook_name,title,location,alias,city,state,postal = [],[],[],[],[],[],[],[],[],[],[],[],[],[]
for ea in data_set:
first.append(ea[0])
second.append(ea[1])
email.append(ea[2])
dept.append(ea[3])
office.append(ea[4])
mobile.append(ea[5])
company.append(ea[6])
outlook_name.append(ea[7])
title.append(ea[8])
location.append(ea[9])
alias.append(ea[10])
city.append(ea[11])
state.append(ea[13])
postal.append(ea[14])
address = pd.DataFrame()
address['Full Name']= outlook_name
address['email']=email
address['Department'] = dept
address['Office No.']=office
address['Mobile No.'] = mobile
address['Company']=company
address['City'] = city
address['Title']= title
address['Office']= location
address['Alias'] = alias
address['First Name']=first
address['Last Name']=second
address['Province'] =state
address['Postal Address'] = postal
address.to_excel('Address_book with '+str(address.shape[0])+' contacts.xlsx',index=None,freeze_panes =(1,0))

You can use the following sequence of actions in the Outlook object model:
Recipient.AddressEntry.GetContact()
The ContactItem class provides all the required properties you are interested in.
To get an instance of the Recipient class you need to use the CreateRecipient method of the Namespace class. For example, a sample VBA macro which shows how to use the method:
Sub ResolveName()
Dim myNamespace As Outlook.NameSpace
Dim myRecipient As Outlook.Recipient
Dim CalendarFolder As Outlook.Folder
Set myNamespace = Application.GetNamespace("MAPI")
Set myRecipient = myNamespace.CreateRecipient("Eugene Astafiev") ' or email address
myRecipient.Resolve
If myRecipient.Resolved Then
Call ShowCalendar(myNamespace, myRecipient)
End If
End Sub
Sub ShowCalendar(myNamespace, myRecipient)
Dim CalendarFolder As Folder
Set CalendarFolder = _
myNamespace.GetSharedDefaultFolder _
(myRecipient, olFolderCalendar)
CalendarFolder.Display
End Sub

Related

Python generated *.msg file

I'm Jan and it's my first post here and the following code is also my first python code. So please don't judge me, if the code is not well shaped :) and don't wonder I had to reduce my mail body.
With the following code I try to generate several msg file depending on a user list called "customer_names". The idea is to iterate through this list and to adjust the email body espacially the placeholder for "Customer". The rest of the body content is not so important. Everything works good except the iteration through the list. I have a suggestion that I may need to increment the index for the list in the loop. Do you have any ideas.
import win32com.client as win32
import datetime
import random
# List of customer names
customer_names = ['Name1','Name2','Name3']
current_customer_index = 0
# List of email providers
email_providers = ['provider1', 'provider2', 'provider3']
# set up the Outlook application
outlook = win32.Dispatch('outlook.application')
# create a new email
mail = outlook.CreateItem(0)
# set the subject and recipients
mail.Subject = "Request for Customer Information"
mail.To = "user#emailprovider.net"
# Message body for the email
message = f"Dear User,\n\nThe information we require from [Customer] is as follows:\n\n- Email Address: [Email Address] \n\nWe kindly request that you send the requested information to us within [number of days] days. \n\n Kind regards."
# set the number of days for the customer to respond
num_days = 7
# set the importance of the email to normal
mail.Importance = 1 # 0=Low, 1=Normal, 2=High
# set the sensitivity of the email to normal
mail.Sensitivity = 0 # 0=Normal, 1=Personal, 2=Private, 3=Confidential
# set the read receipt option to true
mail.ReadReceiptRequested = True
# add a reminder for the sender to follow up in 3 days
mail.FlagRequest = "Follow up"
mail.FlagDueBy = (datetime.date.today() + datetime.timedelta(days=3)).strftime('%m/%d/%Y')
# Generate a random email
for i in range(len(customer_names)):
customer = customer_names[i]
message_with_data = message.replace("[Customer]", customer)
message_with_data = message_with_data.replace("[number of days]", str(num_days))
mail.Body = message_with_data
file_name = "Request_" + customer + ".msg"
file_path = "C:/Users/user/Desktop/Email/" + file_name
mail.SaveAs(file_path)

How to programatic filter global address list in outlook with Python?

I found a way to query all global address in outlook with python,
import win32com.client
import csv
from datetime import datetime
# Outlook
outApp = win32com.client.gencache.EnsureDispatch("Outlook.Application")
outGAL = outApp.Session.GetGlobalAddressList()
entries = outGAL.AddressEntries
# Create a dateID
date_id = (datetime.today()).strftime('%Y%m%d')
# Create empty list to store results
data_set = list()
# Iterate through Outlook address entries
for entry in entries:
if entry.Type == "EX":
user = entry.GetExchangeUser()
if user is not None:
if len(user.FirstName) > 0 and len(user.LastName) > 0:
row = list()
row.append(date_id)
row.append(user.Name)
row.append(user.FirstName)
row.append(user.LastName)
row.append(user.JobTitle)
row.append(user.City)
row.append(user.PrimarySmtpAddress)
try:
row.append(entry.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3a26001e"))
except:
row.append('None')
# Store the user details in data_set
data_set.append(row)
# Print out the result to a csv with headers
with open(date_id + 'outlookGALresults.csv', 'w', newline='', encoding='utf-8') as csv_file:
headers = ['DateID', 'DisplayName', 'FirstName', 'LastName', 'JobTitle', 'City', 'PrimarySmtp', 'Country']
wr = csv.writer(csv_file, delimiter=',')
wr.writerow(headers)
for line in data_set:
wr.writerow(line)
But it querys user one by one and it's very slow. I only need to query user from IT department out of 100,000 users. How can I write the filter to avoid querying all users?
On the Extended MAPI (C++ or Delphi) level, you can access the entries as IMAPITable MAPI interface and retrieve multiple entries in a single call (IMAPITable::QueryRows). You should still keep in mind that you are limited to 32KB on each request, so you'd need to retrieve the data in batches, which is still better than one entry at a time.
IMAPITable is also exposed as AddressEntries.RawTable property in OOM, but since it is Extended MAPI, it can be accessed in C++ or Delphi only.
If using Redemption (any language, I am its author) is an option, it exposes its own MAPITable interface, and in particular ExecSQL method, which allows to retrieve data in a single call.
In VBA:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set GAL = Session.AddressBook.GAL
set Table = GAL.AddressEntries.MAPITable
set Recordset = Table.ExecSQL("SELECT Name, FirstName, LastName, JobTitle, EntryID, " & _
"""http://schemas.microsoft.com/mapi/proptag/0x3A27001f"" As BusinessAddressCity, " & _
"""http://schemas.microsoft.com/mapi/proptag/0x3a26001f"" As BusinessAddressCountry, " & _
"""http://schemas.microsoft.com/mapi/proptag/0x39FE001F"" As PrimarySmtpAddress, " & _
"from list ")
while not Recordset.EOF
Debug.Print(Recordset.Fields("Name").Value & " - " & Recordset.Fields("PrimarySmtpAddress").Value)
Recordset.MoveNext
wend
You mention that you prefer to query the data rather than retrieve all entries - that should be the preferred solution; you should never retrieve all data unless you are creating some kind of synchronization app and you need everything on initial sync. Unfortunately, address book interfaces in MAPI (and hence in Outlook Object Model) are not very flexible when it comes to searching, especially compared to the search capabilities for the messages in folders.
If you want a single matching GAL user, use Application.Session.CreateRecipient / Recipient.Resolve and hope the alias resolves and there are no ambiguous entries. the search will be performed in all address book containers on the search path.
If you want multiple entries, you can filter on a few properties explicitly exposed by GAL for searching (Alias, City, Company, Department, First Name, Last Name, Office, Title). This is what you see in the address book window in Outlook if you click "Advanced Find". Redemption exposes it through the RDOAddressListSearch object:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set AddrList = Session.Addressbook.GAL
set Search = AddrList.Search
Search.FirstName = "John"
Search.City = "Phoenix"
set AddressEntries = Search.GetResults
for each AddressEntry in AddressEntries
Debug.Print AddressEntry.Name
next
#Dmitry Streblechenko 's method solved my problem.
The final code that can work is:
Set Session = CreateObject("Redemption.RDOSession")
Session.Logon
Set GAL = Session.AddressBook.GAL
Set AddrList = Session.AddressBook.GAL
Set Search = AddrList.Search
Search.DisplayName = "renlei"
Set AddressEntries = Search.GetResults
For Each AddressEntry In AddressEntries
Debug.Print AddressEntry.Name
Next

Faster search of email in GAL using Python

I need to create a Python script which grabs different information about 1500 Outlook Contacts (out of 20000), based on their email. Until now, I managed to do this:
def grab_user_details(email):
first_name, last_name, department, location = '', '', '', ''
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
gal = outlook.Session.GetGlobalAddressList()
entries = gal.AddressEntries
for i in entries:
user = i.GetExchangeUser()
if user is not None:
if user.PrimarySmtpAddress == email:
first_name = email.split("#")[0].split(".")[0].title()
last_name = email.split("#")[0].split(".")[1].title()
department = user.Department
location = user.OfficeLocation
print(email, first_name, last_name, department, location)
break
In the end, the code just iterates through the GAL for that specific email. After finding it, it breaks, and continues searching for the next email. This method is fast for emails starting with A, or at least B... but when you have a GAL with 20000 emails, you can't just wait 2 days for it to finish the whole alphabet.
Is there a faster way of doing this?
Thanks!
Use the CreateRecipient method of the Namespace class to get an instance of the Recipient class based on the email address.
Sub TestRecipient()
Dim myNamespace As Outlook.NameSpace
Dim myRecipient As Outlook.Recipient
Set myNamespace = Application.GetNamespace("MAPI")
Set myRecipient = myNamespace.CreateRecipient("address#domain.com")
myRecipient.Resolve
If myRecipient.Resolved Then
' do some stuff here
End If
End Sub
The Recipient.AddressEntry property returns the AddressEntry object corresponding to the resolved recipient. Accessing the AddressEntry property forces resolution of an unresolved recipient name. If the name cannot be resolved, an error is returned. If the recipient is resolved, the Resolved property is True.
Then you can use the AddressEntry.GetExchangeUser method which returns an ExchangeUser object that represents the AddressEntry if the AddressEntry belongs to an Exchange AddressList object such as the Global Address List (GAL) and corresponds to an Exchange user.
Based on #Eugene Astafiev's answer (thank you!), I came up with the following code:
def grab_user_details(email):
name, department, location = '', '', ''
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
user = outlook.Session.CreateRecipient(email)
user.Resolve()
print(user.Resolved)
try:
name = user.AddressEntry.GetExchangeUser().Name
department = user.AddressEntry.GetExchangeUser().Department
location = user.AddressEntry.GetExchangeUser().OfficeLocation
print(email, name, department, location)
except:
print("user NA")
This method is way faster than searching through the GAL.
There's one more thing I need to fix: unfortunately, some users have 2 email address', and .CreateRecipient(email) returns nothing, although outlook can find it. I need to dig into this a little bit more.

How can I add an email to an existing calendar entry using exchangelib?

I have an existing calendar event already scheduled.
I am trying to add a number of emails to that existing event, but when I run this function it adds a new event.
I need to be able to: find the event, add the specified email to the event, have the email added get the invitation.
(I'm new to python so try not to judge harshly)
def updateInvite(pEmail,pSubj,iY, iM, iD, iHH, iMM):
pytz_tz = pytz.timezone('America/New_York')
tz = EWSTimeZone.localzone()
items = account.calendar.view(
start=tz.localize(EWSDateTime(iY, iM, iD, iHH, iMM)),
end=tz.localize(EWSDateTime(iY, iM, iD, iHH + 2, iMM)),
)`
for item in items:
sEventSubj = item.subject
item.save(update_fields=['required_attendees'])
Is called with
pEmail = 'name.last#company.com'
sSubj = 'Invite Test Meeting with Teams link'
iY = 2020
iD = 29
iM = 4
iHH = 16
iMM = 30
updateInvite(pEmail, sSubj, iY, iM, iD, iHH, iMM)
To do this, use an item attachment. Find the message you would like to attach to your calendar item and create an item attachment for it:
from exchangelib import ItemAttachment
# Create some filter to get the emails you want to attach
messages = account.inbox.all()[:5]
# Create a filter go get your calendar item
item = account.calendar.get(subject='Hello Python')
for message in messages:
# Create the attachment and give it a name
attachment = ItemAttachment(name='msg %s' % msg.id[:8], item=message)
item.attach(attachment)

Use python to create rule in Outlook from sender Email address

I am trying to create rules to move emails from a long list of senders to specific folders. For example, if I receive an email from john#email.com, I want it to be moved from "Inbox" to "workstuff\John" (john is a subfolder of workstuff).
I'm using comtypes.clients and python to do this because I found a similar post ( Setting a property using win32com ), in which one of the answers uses comtypes.clients in python.
I am also using https://learn.microsoft.com/en-us/office/vba/outlook/how-to/rules/create-a-rule-to-move-specific-e-mails-to-a-folder as a guideline.
import comtypes.client
o = comtypes.client.CreateObject("Outlook.Application")
rules = o.Session.DefaultStore.GetRules()
rule = rules.Create("Test", 0)
condition = rule.Conditions
condition.From.Recipients.Add(str("fabracht"))
condition.From.Recipients.ResolveAll
#.From.Recipients("fabracht#gmail.com")
condition.Enabled = True
root_folder = o.GetNamespace('MAPI').Folders.Item(1)
dest_folder = root_folder.Folders["Evergreen1"].Folders["Chemistry"]
move = rule.Actions.MoveToFolder
move.__MoveOrCopyRuleAction__com__set_Enabled(True)
move.__MoveOrCopyRuleAction__com__set_Folder(dest_folder)
rules.Save()
I've been able to create the rule, which shows up in outlook. But the rule is missing the "from" part. Basically it says:
" Apply this rule after the message arrives
Move it to the john folder "
I expected the rule to be:
" Apply this rule after the message arrives
From john#email.com
Move it to the john folder "
The article mentioned in your post contains the following code for dealing with the From part:
'Specify the condition in a ToOrFromRuleCondition object
'Condition is if the message is from "Eugene Astafiev"
Set oFromCondition = oRule.Conditions.From
With oFromCondition
.Enabled = True
.Recipients.Add ("Eugene Astafiev")
.Recipients.ResolveAll
End With
The code should look like the following:
import comtypes.client
o = comtypes.client.CreateObject("Outlook.Application")
rules = o.Session.DefaultStore.GetRules()
rule = rules.Create("Test", 0)
condition = rule.Conditions
condition.From.Recipients.Add(str("fabracht"))
condition.From.Recipients.ResolveAll
oFromCondition = oRule.Conditions.From
oFromCondition.Enabled = True
oFromCondition.Recipients.Add("john#email.com")
oFromCondition.Recipients.ResolveAll
#.From.Recipients("fabracht#gmail.com")
condition.Enabled = True
root_folder = o.GetNamespace('MAPI').Folders.Item(1)
dest_folder = root_folder.Folders["Evergreen1"].Folders["Chemistry"]
move = rule.Actions.MoveToFolder
move.__MoveOrCopyRuleAction__com__set_Enabled(True)
move.__MoveOrCopyRuleAction__com__set_Folder(dest_folder)
rules.Save()

Categories