function send_email_ms365
Sends an email through Microsoft 365 Graph API with support for HTML content, multiple recipients (to/cc/bcc), and file attachments.
/tf/active/vicechatdev/CDocs/utils/notifications.py
446 - 543
moderate
Purpose
This function provides a complete email sending solution using Microsoft 365's Graph API. It handles authentication token retrieval, constructs properly formatted email messages with HTML bodies, manages multiple recipient types (to, cc, bcc), and supports file attachments by encoding them in base64. The function is designed for applications that need to send emails programmatically through Microsoft 365 accounts, with comprehensive error handling and logging.
Source Code
def send_email_ms365(to_addresses: List[str],
subject: str,
body_html: str,
body_text: str = None,
cc_addresses: List[str] = None,
bcc_addresses: List[str] = None,
attachments: List[Dict[str, Any]] = None) -> bool:
"""
Send email through Microsoft 365 Graph API.
Args:
to_addresses: List of recipient email addresses
subject: Email subject
body_html: HTML body content
body_text: Optional plain text body
cc_addresses: Optional list of CC recipients
bcc_addresses: Optional list of BCC recipients
attachments: Optional list of attachments as dictionaries with keys:
'filename', 'content' (bytes), 'content_type'
Returns:
Boolean indicating success
"""
try:
# Get token
token = get_ms365_token()
if not token:
logger.error("Failed to get MS365 token")
return False
# Prepare headers
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
# Prepare email data
email_data = {
'message': {
'subject': subject,
'body': {
'contentType': 'HTML',
'content': body_html
},
'toRecipients': [{'emailAddress': {'address': email}} for email in to_addresses]
},
'saveToSentItems': 'true'
}
# Add CC if provided
if cc_addresses:
email_data['message']['ccRecipients'] = [
{'emailAddress': {'address': email}} for email in cc_addresses
]
# Add BCC if provided
if bcc_addresses:
email_data['message']['bccRecipients'] = [
{'emailAddress': {'address': email}} for email in bcc_addresses
]
# Add attachments if provided
if attachments:
email_data['message']['attachments'] = []
for attachment in attachments:
# Convert content to base64
content_bytes = attachment['content']
if isinstance(content_bytes, str):
content_bytes = content_bytes.encode('utf-8')
content_b64 = base64.b64encode(content_bytes).decode('utf-8')
attachment_data = {
'@odata.type': '#microsoft.graph.fileAttachment',
'name': attachment['filename'],
'contentType': attachment.get('content_type', 'application/octet-stream'),
'contentBytes': content_b64
}
email_data['message']['attachments'].append(attachment_data)
#logger.info("headers and email_data: ", headers, email_data)
# Send email through MS Graph API
#if 'wim@vicebio.com' in to_addresses or 'wim.tiest@oneco.be' in to_addresses:
if True:
response = requests.post(
'https://graph.microsoft.com/v1.0/users/' + settings.MS365_SENDER_EMAIL + '/sendMail',
headers=headers,
json=email_data
)
if response.status_code >= 200 and response.status_code < 300:
return True
else:
logger.error(f"MS365 API error: {response.status_code}, {response.text}")
return False
return True
except Exception as e:
logger.error(f"Error sending email through MS365: {e}")
return False
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
to_addresses |
List[str] | - | positional_or_keyword |
subject |
str | - | positional_or_keyword |
body_html |
str | - | positional_or_keyword |
body_text |
str | None | positional_or_keyword |
cc_addresses |
List[str] | None | positional_or_keyword |
bcc_addresses |
List[str] | None | positional_or_keyword |
attachments |
List[Dict[str, Any]] | None | positional_or_keyword |
Parameter Details
to_addresses: List of email addresses for primary recipients. Must be valid email address strings. At least one recipient is required.
subject: The subject line of the email as a string. Can be any text content.
body_html: The main email body content in HTML format. This is the primary content that will be displayed to recipients.
body_text: Optional plain text version of the email body. Currently not used in the implementation but provided for potential fallback scenarios. Defaults to None.
cc_addresses: Optional list of email addresses to be copied (CC) on the email. Must be valid email address strings. Defaults to None.
bcc_addresses: Optional list of email addresses to be blind copied (BCC) on the email. Must be valid email address strings. Defaults to None.
attachments: Optional list of dictionaries, where each dictionary represents a file attachment with keys: 'filename' (str, name of the file), 'content' (bytes or str, file content), and 'content_type' (str, MIME type, defaults to 'application/octet-stream'). Content is automatically base64-encoded. Defaults to None.
Return Value
Type: bool
Returns a boolean value: True if the email was successfully sent (HTTP status code 200-299 from MS Graph API), False if there was any error including token retrieval failure, API errors, or exceptions during processing.
Dependencies
requestsmsalbase64logging
Required Imports
import requests
import base64
import logging
from typing import List, Dict, Any
Conditional/Optional Imports
These imports are only needed under specific conditions:
from CDocs.config import settings
Condition: Required for MS365_SENDER_EMAIL configuration setting
Required (conditional)Custom get_ms365_token() function
Condition: Required for obtaining Microsoft 365 authentication token
Required (conditional)Usage Example
# Basic email sending
result = send_email_ms365(
to_addresses=['recipient@example.com'],
subject='Test Email',
body_html='<h1>Hello</h1><p>This is a test email.</p>'
)
# Email with CC, BCC, and attachments
with open('report.pdf', 'rb') as f:
pdf_content = f.read()
attachments = [
{
'filename': 'report.pdf',
'content': pdf_content,
'content_type': 'application/pdf'
}
]
result = send_email_ms365(
to_addresses=['recipient1@example.com', 'recipient2@example.com'],
subject='Monthly Report',
body_html='<p>Please find the attached report.</p>',
cc_addresses=['manager@example.com'],
bcc_addresses=['archive@example.com'],
attachments=attachments
)
if result:
print('Email sent successfully')
else:
print('Failed to send email')
Best Practices
- Ensure get_ms365_token() function is properly implemented and returns valid OAuth tokens
- Validate email addresses before passing them to the function to avoid API errors
- Handle the boolean return value to implement proper error handling and retry logic
- Be aware of Microsoft Graph API rate limits when sending bulk emails
- Ensure attachment content is in bytes format; the function handles string-to-bytes conversion but bytes are preferred
- The function saves sent emails to the Sent Items folder by default
- Monitor logger output for detailed error messages when emails fail to send
- Note that body_text parameter is currently unused; only HTML body is sent
- The function contains commented-out conditional logic (if True:) that may need review for production use
- Ensure proper Azure AD app permissions are granted for the Mail.Send scope
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function gen_send_email 75.4% similar
-
function send_email_smtp 72.8% similar
-
class O365Client 60.8% similar
-
function test_send_email 56.2% similar
-
function get_ms365_token 53.6% similar