circuit

How to Filter Outlook Emails With a Subject Using Python

Use MSGraph search API query to filter email messages.


Introduction:

This article describes how to filter email messages from an Outlook mailbox using Python. We will be using the following libraries to accomplish this task.

https://github.com/AzureAD/microsoft-authentication-library-for-python

Note: Please note that if you are using ADAL for authentication, Microsoft recommends migrating to MSAL.

Requirements:

To filter emails with a subject, we need the following.

  • Mailbox credentials (username and password)
  • Sender email (Filter messages from a specific sender if required)
  • Message id (Unique message id for an email)

The above data is required to authenticate, initialise the ClientApplication object and construct the MS Graph API to download the attachments.

Install MSAL:

$pip install msal

The Microsoft authentication library for python allows you to sign in users or apps with Microsoft identities, obtain auth tokens to be used with Microsoft Graph APIs. They are built using OAuth2 and OpenID connect protocols.

Initialising the Client Application:

MSAL defines 3 types of applications and clearly provides a demarcation in initialising them.

  • Client Application
  • PublicClientApplication
  • ConfidentialClientApplication

To learn more about the OAuth client types please click here. In this article, we will be using ClientApplication to initialise the app object and reuse throughout our application.

from msal import ClientApplication

class AttachmentDownloader:

def __init__(self, username: str, password: str):

self.client_id = '<your client id>'

self.authority = 'https://login.microsoftonline.com/<tenant-name>'

# Initialise MS ClientApplication object with your client_id and authority URL

self.app = ClientApplication(client_id=self.client_id,

authority=self.authority)

self.username = username # your mailbox username

self.password = password # your mailbox password

if __name__ == "__main__":

downloader = AttachmentDownloader("username@outlook.com", "password")

Acquire token:

Now that we have our app object initialised, we can acquire the token. This token can be used to extract to access_token for headers.

token = self.app.acquire_token_by_username_password(username=self.username,

password=self.password,

scopes=['.default'])

print(token)

Output:

The token output looks like this.

{

"token_type":"Bearer",

"scope":"email openid profile 00000003-0000-0000-c000-000000000000/EWS.AccessAsUser.All 00000003-0000-0000-c000-000000000000/IMAP.AccessAsUser.All 00000003-0000-0000-c000-000000000000/Mail.Read-0000-c000-000000000000/Mail.Read.Shared 00000003-0000-0000-c000-000000000000/Mail.ReadWrite.Shared 00000003-0000-0000-c000-000000000000/Mail.Send 00000003-0000-0000-c000-000000000000/Mail.Send.Shared 00000003-0000-0000-c000-000000000000/POP.AccessAsUser.All 00000003-0000-0000-c0/User.Read 00000003-0000-0000-c000-000000000000/.default",

"expires_in":4914,

"ext_expires_in":4914,

"access_token":"eyJ0eXAiOiJKV1QiLCJub25jZSI6InJKaWVzUE9ERGNXTjItZlIwQTRTWVFoV2t6aVEyelFENmlMS2N1M2xycFUiLCJhbGciOiJSUzI1NiIsIng1dCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyIsImtpZCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyJ9-YQwnl0SIOht0EVcKtJuAOrMUP4xsR0uNBInGcpob9r9Pt_ZX6z_Jw412TIxdBw",

"refresh_token":"0.ARMAyjiRs.AgABAAAAAAD--DLA3VO7QrddgJg7WevrAgDs_wQA9P_9CM2vmlsFp62-YzCVROSVA-HK0F0KUqGrlLA-t-s8KOlN-elmtVBhSaVj1KvuqtxSH-lVvchKt4ZSy1aFGodMGo6M5A2a0k7E7xJgTlqeRSrS7Cq-UTekMTIzIUly7F6euyyJi1XeMLhB7Uhr-Dk_Y3pYVNn6Wy_pZOcracO-7WqlrbUQGg0bSbv-",

"id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpTMVhvMU9XRGpfNTJ2YndHTmd2UU8yVnpNYyJ9.eyJhdWQiOiIwMWZlMmVjOC01MzYzLTQ2YmUtYjEyNC01MWUwZTAxOWMwMGIiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjM5MTM4Y2EtM2NlZS00YjRhLWE0ZDYtY2Q4M2Q5ZGQ",

"client_info":"eyJ1aWQiOiI4NzMwYjc5Ni1mNDRkk",

"id_token_claims":{

"aud":"01fe2ec8-5363-46be-b124-e019c00b",

"iss":"https://login.microsoftonline.com/b39138ca-3cee-/v2.0",

"iat":1648185788,

"nbf":1648185788,

"exp":1648189688,

"name":"USER",

"oid":"8730b796-f44d-4f3d-8b01-9e201055d039",

"preferred_username":"user@outlook.com",

"rh":"0.ARMAyjiRs-.",

"sub":"Z5RogClxDJWqQ",

"tid":"b39138ca-3cee-4b4a-a4d6-cd83d9dd62f0",

"uti":"AOkSTATnukSA",

"ver":"2.0"

}

}

Extract emails from a sender with a subject:

Now that we have initialised our client application and have all the information we need to access msal, let's extract emails from a sender with specific subject.

import requests

from requests import RequestException

from msal import ClientApplication

def get_email_messages(subject):

try:

app = ClientApplication(client_id='your client id',

authority='your authority url')

token = app.acquire_token_by_username_password(username='username', password='password',

scopes=['.default'])

headers = {"Authorization": f"Bearer {token.get('access_token')}"}

# filter the most recent email.

params = {"$search": f'"subject:{subject}"',

"top": 1}

response = requests.get(url='https://graph.microsoft.com/v1.0/me/messages',

headers=headers,

params=params)

return response.json()

except RequestException as re:

print(f" Exception while getting email messages : {re} ")

We pass the subject of interest in the query parameters of the API. Be mindful of the quotes used here. You could really shoot yourself in the foot here as the API may not complain in case of quotes mismatch rather it just returns incorrect results.

The top parameters limits the number of results returned. Let's say we have multiple results from the API response for the same subject, setting top=1 gives you the latest email 😉. Just what we want eh!!!

The API looks like below.

https://graph.microsoft.com/v1.0/me/messages?$search="subject:Test subject email"&top=1

If you wanna use curl or postman, this may come in handy. Here, we have a used a $search query parameter that can be applied to MS Graph APIs.

Microsoft Graph supports optional query parameters that you can use to specify and control the amount of data returned in a response. The support for the exact query parameters varies from one API operation to another, and depending on the API.

You can search messages based on a value in specific message properties. The results of the search are sorted by the date and time that the message was sent. A $search request returns up to 1000 results.

Output:

{

"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('a1049f67-e41a-4c43-961-4604e17')/messages",

"@odata.nextLink": "https://graph.microsoft.com/v1.0/me/messages?$search=%22subject%3aTest+subject+email%22&top=1&$skiptoken=MSZZVlF4YlZwcVVUVmFWR2N4V1hreGFscHFVWGxNSGc9",

"value": [

{

"@odata.etag": "W/\"CQAAABYAAAB4JTob+Wwd80MYwNQJT\"",

"id": "AAMkADA4NWYzMjliLWFkNzQtNGEzYS1hOTETYxNWQ3MGYyYmEzOABGAAAAAAAcrUN9WKzQR46Vn6UoXJxfBwBUaFNIx94JTob_Wwd80MYHAAAAAAEJAABUaFNIx94JTob_Wwd80MYHAACwp1kOAAA=",

"createdDateTime": "2022-07-26T10:14:07Z",

"lastModifiedDateTime": "2022-07-26T10:16:26Z",

"changeKey": "CQAAABYAAABob+Wwd80MYHAACwNQJT",

"categories": [],

"receivedDateTime": "2022-07-26T10:16:14Z",

"sentDateTime": "2022-07-26T10:16:13Z",

"hasAttachments": true,

"internetMessageId": "<DM6PR05MB648CC2949@DM6PR05MB6442.namprd05.prod.outlook.com>",

"subject": "Test subject email",

"bodyPreview": "",

"importance": "normal",

"parentFolderId": "AQMkADA4NWYzMjliLWFkNzQtNGEzYS1hOTE0LTYxNWQ3MGYyYmEzOA-lsHfNDGBwAAAgEJAAAA",

"conversationId": "AAQkADA4NWYzMjliLWFkNzQmEzOAAQAOWDumb5AwZMq2c7eQHxq38=",

"conversationIndex": "AQHYoNhx5YO6ZvkDBkyrZzt5AfGrfw==",

"isDeliveryReceiptRequested": false,

"isReadReceiptRequested": false,

"isRead": true,

"isDraft": false,

"webLink": "https://outlook.office365.com/owa/?ItemID=AAMkADA4NWYzMjliLWFkNzQ%2BWwd80MYHAACwp1kOAAA%3D&exvsurl=1&viewmodel=ReadMessageItem",

"inferenceClassification": "focused",

"body": {

"contentType": "html",

"content": "<html><head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><meta name=\"Generator\" content=\"Microsoft Word 15 (filtered medium)\"><style>\r\n<!--\r\n@font-face\r\n\t{font-family:\"Cambria Math\"}\r\n@font-face\r\n\t{font-family:Calibri}\r\np.MsoNormal, li.MsoNormal, div.MsoNormal\r\n\t{margin:0cm;\r\n\tfont-size:11.0pt;\r\n\tfont-family:\"Calibri\",sans-serif}\r\nspan.EmailStyle17\r\n\t{font-family:\"Calibri\",sans-serif;\r\n\tcolor:windowtext}\r\n.MsoChpDefault\r\n\t{font-family:\"Calibri\",sans-serif}\r\n@page WordSection1\r\n\t{margin:72.0pt 72.0pt 72.0pt 72.0pt}\r\ndiv.WordSection1\r\n\t{}\r\n-->\r\n</style></head><body lang=\"EN-IN\" link=\"#0563C1\" vlink=\"#954F72\" style=\"word-wrap:break-word\"><div class=\"WordSection1\"><p class=\"MsoNormal\">&nbsp;</p></div></body></html>"

},

"sender": {

"emailAddress": {

"name": "sender",

"address": "sender@outlook.com"

}

},

"from": {

"emailAddress": {

"name": "Dinesh",

"address": "dinesh@outlook.com"

}

},

"toRecipients": [

{

"emailAddress": {

"name": "sender",

"address": "sender@outlook.com"

}

}

],

"ccRecipients": [],

"bccRecipients": [],

"replyTo": [],

"flag": {

"flagStatus": "notFlagged"

}

}

]

}

Summary:

  • Create a ClientApplication object and use it throughout the lifecycle of our application.
  • Use the app object to extract all email from a specific sender with a subject.

Related Articles:

If you are looking for how to download the messages/attachments after filtering, please read the below article.

References:




Continue Learning