【问题标题】:How to send python email.message.EmailMessage with Microsoft Graph API如何使用 Microsoft Graph API 发送 python email.message.EmailMessage
【发布时间】:2022-12-09 03:59:32
【问题描述】:

我们的网络应用程序通过 python 的 smtplib 发送格式良好的消息(包括一些嵌入的图像)。这些消息是使用 python 的 email.message.EmailMessage 类生成的,如下面的代码所示。

10 月初,Microsoft deprecated support for Basic Auth 在他们的 Office365 电子邮件帐户中。我们一直在使用 Basic Auth,现在需要找到一个新的解决方案。在struggling with getting OAuth2 working for some time 之后,我们决定重构并改用Microsoft Graph API

奇怪的是,尽管 Graph API 站点上的大多数示例都包含多语言(HTTP / C# / Javascript / PHP 等)示例,但用于发送具有 MIME 格式的电子邮件的示例(Examlpe 4)只有一个 HTTP 示例。

我们想知道是否可以使用图形 API 发送我们使用 python.email.EmailMessage 构建的电子邮件,如果可以,如何发送。

下面是一些示例代码,显示了我们之前所做的以及我们现在正在尝试获得的内容。

当我们运行代码时,我们得到错误

'{"error":{"code":"RequestBodyRead","message":"Requested value \'text/plain\' was not found."}}'

import smtplib
from email.utils import formatdate
from email.message import EmailMessage
import requests


server = 'smtp.office365.com'  # for exampler
port = 587  # for example
from_mail = 'levrai@ninjane.er'
to_mail = 'desti@nati.on'
subject = 'Demo sending the old way!'
password = 'not_so_Secur3!'

message_parts = ['Hi sir', 'This is a demo message.', 'It could help others to help me, and possbily others too.']

# the below function builds up the nice message based on an html template
text_msg, html_msg, cids, locs = doc_mail_from_template(message_parts)  

msg = EmailMessage()
msg.set_content(text_message)
msg.add_alternative(html_msg, subtype='html')
msg['From'] = from_mail
msg['To'] = to_mail
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject

# now embed images to the email
for loc, cid in zip(locs, cids):
    with open(loc, 'rb') as img:
        maintype, subtype = guess_type(img.name)[0].split('/')  # know the Content-Type of the image
        msg.get_payload()[1].add_related(img.read(), maintype=maintype, subtype=subtype, cid=cid) # attach it


if date_now < '2022-10-01':   # before, we could do this
    with smtplib.SMTP(server, port) as smtp:
        smtp.starttls(context=context)
        smtp.login(from_mail, password)
        smtp.sendmail(from_mail, [to_mail, ], msg.as_string())
        
else:  # now we must do this
    client_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx8c5'
    client_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxb96'
    tenant_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx973'
    userId = "levrai@ninjane.er"        
    authority = f"https://login.microsoftonline.com/{tenant_id}"
    scopes = ["https://graph.microsoft.com/.default"]
    
    app = msal.ConfidentialClientApplication(client_id=client_id, client_credential=client_secret, authority=authority)
    result = app.acquire_token_silent(scopes, account=None)
    if not result:
        result = app.acquire_token_for_client(scopes=scopes)
    
    # setup message:
    email_msg = {'Message': {'Subject': subject,
                             'Body': {
                                 'ContentType': 'text/plain', 'Content': e_message.as_string() },   # what do i put here?
                             'ToRecipients': [{'EmailAddress': {'Address': to_mail}}]
                             },
                'SaveToSentItems': 'true'}
    endpoint = f'https://graph.microsoft.com/v1.0/users/{from_user}/sendMail'
    r = requests.post(endpoint, json=email_msg,
                      headers={'Authorization': 'Bearer ' + result['access_token'], "Content-Type": "application/json"})

【问题讨论】:

    标签: python email smtp microsoft-graph-api mime-types


    【解决方案1】:

    您似乎在使用 Graph 作为 JSON 消息发送的示例与作为 MIME 消息发送混合在一起。以 MIME 格式发送有一些限制,例如您的电子邮件必须小于 4 mb(或者因为 MIME 膨胀 2.5-3 mb)。您需要确保对消息进行正确编码,但在从 EmailMessage 构建的 phython 中,以下内容对我来说没问题。 (Microsoft 方面的解析器可能有点限制,所以从简单的格式开始,看看是否有效,然后增加消息的复杂性)。

    例如

    from email.message import EmailMessage
    import base64
    import sys  
    import json
    import logging
    import msal
    import requests
    
    
    config = {
        "authority": "https://login.microsoftonline.com/eb8db77e-65e0-4fc3-b967-.......",
        "client_id": "18bb3888-dad0-4997-96b1-.........",
        "scope": ["https://graph.microsoft.com/.default"],
        "secret": ".............",
        "tenant-id": "eb8db77e-65e0-4fc3-b967-........"
    }
    
    app = msal.ConfidentialClientApplication(config['client_id'], authority=config['authority'],
                                                 client_credential=config['secret'])
    result = app.acquire_token_silent(config["scope"], account=None)
    
    if not result:
        logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
        result = app.acquire_token_for_client(scopes=config["scope"])
    
    sender_email = "gscales@gbbbbb.onmicrosoft.com"
    receiver_email = "gscales@ccccc.onmicrosoft.com"
    
    
    def create_message(sender, to, subject, message_text):
    
        message = EmailMessage()
        message.set_content(message_text)
        message['to'] = to
        message['from'] = sender
        message['subject'] = subject
        raw = base64.urlsafe_b64encode(message.as_bytes())
        return raw.decode()
    
    messageToSend = create_message(sender_email,receiver_email,"test subject","test 123")
    
    print(messageToSend)
     
    endpoint = f'https://graph.microsoft.com/v1.0/users/{sender_email}/sendMail'
    r = requests.post(endpoint, data=messageToSend,
                          headers={'Authorization': 'Bearer ' + result['access_token'], "Content-Type": "text/plain"})
    print(r.status_code)
    print(r.text)

    【讨论】:

      【解决方案2】:

      @Glen Scales 我遇到了一个问题,我不断收到一条错误消息,上面写着 {"error":{"code":"BadRequest","message":"Resource not found for the segment 'jtarter5'.," innerError":{"date":"2022-12-08T19:32:45","re​​quest-id":"bc962d02-1310-4963-a684-56c43484a3c8","client-request-id":"bc962d02-1310 -4963-a684-56c43484a3c8"}}} 当我运行您提供的代码时。

      如果我在我公司的 VPN 上,它会给我这个错误:gaierror: [Errno 11001] getaddrinfo failed。

      我在 Azure 中创建了一个应用程序注册以获取客户端、机密等,当我在 VPN 之外运行它时它似乎会生成一个令牌,但在 VPN 上什至无法连接。

      有什么建议么?将不胜感激。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-02
        • 1970-01-01
        相关资源
        最近更新 更多