以编程方式发布包含用户指定的 HTML 和纯文本正文的 Gmail 草稿

Posted

技术标签:

【中文标题】以编程方式发布包含用户指定的 HTML 和纯文本正文的 Gmail 草稿【英文标题】:Programmatically posting Gmail draft with both HTML and plaintext bodies specified by user 【发布时间】:2016-05-19 23:44:47 【问题描述】:

我正在使用 Gmail API 在 Python 中自动创建 Gmail 草稿。我需要创建 html 格式的电子邮件,但我个人也需要创建纯文本后备,因为这是正确的做法。

我以为我已经完成了上述所有工作,直到我尝试使纯文本回退与 HTML 稍有不同。似乎谷歌自己为我创建了纯文本后备,而不是使用我提供的,所以如果我的 html 正文是 <HTML><BODY>HTML Body</BODY></HTML> 并且我的纯文本正文是 Plaintext body,最终的纯文本正文将是 @987654324 @,丢弃我提供的明文。

我的问题:有谁知道如何让 Gmail API 使用 提供的纯文本,而不是为我自动生成回退?

我注意到一个相关的项目:如果我以不同的顺序附加 HTML 和纯文本正文,则会发生相反的情况 - GMail 将根据我的纯文本自动生成 HTML 正文。所以它似乎只关注最后一个附加的身体。

我正在使用的代码的精简版:

import base64
import os
import httplib2
import oauth2client
from oauth2client import client
from oauth2client import tools
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from pathlib import Path
from apiclient import errors
from apiclient import discovery

SCOPES = 'https://mail.google.com/'
CLIENT_SECRET_FILE = 'client_id.json'
APPLICATION_NAME = 'Test Client'

def get_credentials():
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir, 'gmail-python-quickstart.json')

    store = oauth2client.file.Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else:  # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials


def CreateDraft(service, user_id, message_body):
    message = 'message': message_body
    draft = service.users().drafts().create(userId=user_id, body=message).execute()
    return draft


def CreateTestMessage(sender, to, subject):
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = to
    plain_text = "Text email message. It's plain. It's text."
    html_text = """\
<html>
  <head></head>
  <body>
    <p>HTML email message</p>
    <ol>
        <li>As easy as one</li>
        <li>two</li>
        <li>three!</li>
    </ol>
    <p>Includes <a href="http://***.com/">linktacular</a> goodness</p>
  </body>
</html>
"""

    # Swapping the following two lines results in Gmail generating HTML
    # based on plaintext, as opposed to generating plaintext based on HTML
    msg.attach(MIMEText(plain_text, 'plain')) 
    msg.attach(MIMEText(html_text, 'html'))

    print('-----\nHere is the message:\n\nm'.format(m=msg))
    encoded = base64.urlsafe_b64encode(msg.as_string().encode('UTF-8')).decode('UTF-8') 
    return 'raw': encoded


def main():
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('gmail', 'v1', http=http)

    my_address = 'example@gmail.com' # Obscured to protect the culpable

    test_message = CreateTestMessage(sender=my_address,
                                 to='example@gmail.com',
                                 subject='Subject line Here')

    draft = CreateDraft(service, my_address, test_message)


if __name__ == '__main__':
    main()

更新:以下是example gists 我发送 Gmail 的内容与 Gmail 发送的内容,包括 HTML-then-plaintext 和纯文本-then-HTML 订单(生成不同的结果)

【问题讨论】:

能否以自己的邮箱为接收者创建草稿,发送,然后查看发送的带有show original的消息并显示内容?会很有趣。 :) 我确实可以。事实上,这就是我发现差异的方式。使用示例草稿链接更新帖子。 另外,礼仪/风格问题:最好将这样的上下文作为外部链接(例如要点)包含在原始帖子中吗?我担心主帖太长,但我也担心可能整个上下文都应该在主帖中提供。 老实说,我不知道这样做的礼仪。我喜欢现在的方式,因为如你所说,要点相当大。 :) 我很欣赏这个健全的检查。我没有尝试只发送,因为这个特殊的用例要求创建草稿,然后在发送之前通过一个 QA 流程。很高兴知道如果我直接发送,我会做正确的事情! 【参考方案1】:

TL;DR:没有。

Draft 对象与 Web UI 和移动 UI 共享,如果 text/plain 不仅仅是 text/html 的简单转换,那么一旦用户使用任何其他 UI 编辑消息,特殊定制将是丢失。使用草稿 UI 的原因是允许用户在其他界面之间共享这些草稿。

如果您不关心/不想要该功能,请不要使用草稿并在最后发送()它,这就像 SMTP-MSA 一样允许更大的灵活性。

【讨论】:

我们要求这些电子邮件在发送前由质量保证人员进行审查,因此除非我们能找到另一种审查最终表格的方法,否则不能直接使用 send()发送之前的电子邮件。鉴于这些限制,我认为我最好的解决方案是“只需发布 HTML 版本,并为 Gmail 自动提供文本回退感到高兴” 是的,我认为这行得通。如果您想要自定义文本/纯文本转换,请在另一个论坛中存储/查看。或者是的,正如您提到的,只查看 text/html 并假设 Gmail 将继续提供合理的 text/plain 转换(这对于绝大多数用例来说非常好且合理)。

以上是关于以编程方式发布包含用户指定的 HTML 和纯文本正文的 Gmail 草稿的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式用户根据 JavaFX 中的字符串值定义列表视图的颜色

如何以编程方式仅选择用户可选择的所有文本节点

HTML学习摘要1

以编程方式将键盘大写锁定按钮设置为 ON? [复制]

以编程方式计算 UITableViewCell 高度包含“<br>”或“\n”的文本

Rails:如何在多部分/替代电子邮件中使用部分(HTML 和纯文本)