pythonazureemailoutlookmicrosoft-graph-api

How to send emails via Outlook client that includes user's email signatures?


I'm currently using MS Graph API to send emails, but this doesn't include email signatures. I have explored/me/mailBoxSettings API to check for signatures, but it doesn't have any info about signatures. I need to create a Django API for a React frontend client to send emails. Do I need to explore third-party services like SendGrid or custom SMTP server?


Solution

  • I found a way rather a workaround to fetch the signature by using the sent items of user's mailbox. Approach:

    1. Fetch the sent mails from the user's sent items folder using https://graph.microsoft.com/v1.0/me/mailFolders/sentitems/messages?$top=10
    2. This returns the top 10 sent mails.
    3. Use BeautifulSoup library to extract the HTML content of the mail using Signature as id.
    4. After extracting the signature, append the same as an inline attachment to the email body.
    5. If the signature is not found, you can use the sender's name as the signature.
        def send_email(self, access_token, to_email, subject, body):
            """
            Send email using access token and include the signature image as an inline attachment.
            :param access_token: OAuth access token
            :param to_email: Recipient email address
            :param subject: Email subject
            :param body: Email body
            :return: Boolean indicating success
            """
            headers = {
                'Authorization': f'Bearer {access_token}',
                'Content-Type': 'application/json'
            }
            inline_attachments = []
            try:
                # Step 1: Fetch the last sent email to extract the signature
                response = requests.get(
                    f'https://graph.microsoft.com/v1.0/me/mailFolders/sentitems/messages?$top=1',
                    headers=headers
                )
                # print(f"response: {response.json()}")
    
                if response.status_code == 200:
                    response_body = response.json()['value'][0]['body']['content']
                    message_id = response.json()['value'][0]['id']
                    sender_name = response.json()['value'][0]['sender']['emailAddress']['name']
                    user_name=sender_name.split(", ")
                    print(f"User name: {user_name}")
                    soup = BeautifulSoup(response_body, 'html.parser')
    
                    # Step 2: Extract the signature element
                    signature_elements = soup.find_all(id="Signature")
                    # print(f"signature elements: {signature_elements}")
                    signature_element = None
                    for signature in signature_elements:
                        print(f"Signature text: {signature.get_text()}")
                        for name in user_name:
                            if name in signature.get_text():
                                print(f"Matching signature found.{name}")
                                signature_element = signature
                                break
    
                    if signature_element:
                        # print(f"Signature HTML found.{signature_element}")
    
                        # Step 3: Handle inline images in the signature
                        img_tags = signature_element.find_all("img", {"src": lambda x: x and x.startswith("cid:")})
                        
                        for img_tag in img_tags:
                            cid = img_tag["src"].replace("cid:", "")  # Extract the Content-ID
    
                            # Fetch the attachment using the Content-ID
                            attachment_response = requests.get(
                                f'{self.graph_endpoint}/me/messages/{message_id}/attachments',
                                headers=headers
                            )
                            if attachment_response.status_code == 200:
                                attachments = attachment_response.json().get("value", [])
                                for attachment in attachments:
                                    if attachment.get("contentId") == cid:
                                        # Decode the base64 content of the attachment
                                        image_data = attachment.get("contentBytes")
                                        content_type = attachment.get("contentType")
    
                                        # Add the image as an inline attachment
                                        inline_attachment = {
                                            "@odata.type": "#microsoft.graph.fileAttachment",
                                            "name": attachment.get("name"),
                                            "contentBytes": image_data,
                                            "contentType": content_type,
                                            "contentId": cid,
                                            "isInline": True
                                        }
                                        inline_attachments.append(inline_attachment)
                                        break
    
                        # Convert the signature element back to HTML
                        user_signature = str(signature_element)
                    else:
                        print("No signature found in the email.")
                        user_signature = None
                else:
                    print(f"Failed to fetch the last sent email. Status code: {response.status_code}")
                    user_signature = None
    
                # Step 4: Append the signature to the email body
                if user_signature:
                    body += f"<br>{user_signature}"
    
                # Step 5: Construct the email message
                email_message = {
                    "message": {
                        "subject": subject,
                        "body": {
                            "contentType": "HTML",
                            "content": body
                        },
                        "toRecipients": [
                            {
                                "emailAddress": {
                                    "address": to_email
                                }
                            }
                        ],
                        "attachments": inline_attachments  # Add inline attachments here
                    },
                    "saveToSentItems": "true"
                }
    
                # Step 6: Send the email
                mail_response = requests.post(
                    f'https://graph.microsoft.com/v1.0/me/sendMail',
                    json=email_message,
                    headers=headers
                )
                if mail_response.status_code in [200, 201, 202]:
                    print("Email sent successfully with inline image!")
                    return True
                else:
                    print(f"Failed to send email. Status code: {mail_response.status_code}")
                    print("Response:", mail_response.text)
                    return False
    
            except Exception as e:
                print(f"Error in sending email: {str(e)}")
                return False