使用 Gmail API Python 发送回复

问题描述 投票:0回答:3

我创建了两个 Gmail 帐户,并尝试使用 Python Gmail API 在它们之间创建一个电子邮件线程。

我可以毫无问题地发送电子邮件,但是当涉及到相互回复和创建线程时,它根本不起作用:新消息已成功显示为发件人收到的电子邮件的答案,但它对于接收者来说,显示为一条新消息 - 没有链接的线程。

此问题已于 2019 年在此描述:https://stackoverflow.com/a/63186609/21966625

但是,自本文发布以来,Gmail API 发生了很大变化,我没有找到如何在今天的 API 中使用这些建议。

我尝试仔细遵守docs的说明,在回复时将消息的参数

References
In-Reply-To
定义为收到消息的id。

确实,我检索了电子邮件:

received_email= service.users().messages().get(userId='me', id=label['id']).execute()

我得到一个看起来像这样的字典:

{'id': '189462395f418017', 'threadId': '189462395f418017', 'labelIds': ['UNREAD','INBOX'], 'snippet': 'xxx'....}

因此,当我构建电子邮件时,以下方法应该有效:

message_id=received_email['id']

message = EmailMessage()
message.set_content('')
message['To'] = '[email protected]'
message['From'] = '[email protected]'
message['References'] = message_id
message['In-Reply-To'] = message_id
message['Subject'] = 'Automated draft'

以同样的方式,我将

threadId
定义为我要回复的消息的id。

create_message = {'raw': encoded_message,
                  'threadId': message_id
                 }
send_message = (service.users().messages().send(userId="me", body=create_message).execute())

由于这部分代码,答案可以正确显示(对于答案的发送者),如上所述,但对于接收者来说,它显示为一条新消息 - 未链接到线程。

python gmail gmail-api google-api-python-client
3个回答
5
投票

其实我找到了为什么我的方法不起作用;即使字典提到了一种消息 ID :

email = {'id': '189462395f418017', 'threadId': '189462395f418017', 'labelIds': ['UNREAD','INBOX'], 'snippet': 'xxx'....}

我以为

messageID
可以通过电话来获取
email['id']

真正的

messageID
位于
['payload']['headers']
字典中的某个位置;人们可以通过像这样的循环找到它:

for p in email['payload']['headers']:
    if p["name"] == "Message-Id":
        message_id = p['value']

这样我们就得到了电子邮件的真实

messageID
,并且线程已成功创建。


2
投票

您需要将回复消息作为主题的一部分发送。您需要从原始消息中识别线程 ID,而不是消息 ID,并将其添加为新消息的消息对象的一部分...

create_message = {
    'raw': encoded_message,
    'threadId': threadId
}

https://developers.google.com/gmail/api/guides/threads https://developers.google.com/gmail/api/reference/rest/v1/users.messages#Message

您可能还需要包含标题“References”和“In-Reply-To”以及最后一条消息的消息 ID。


1
投票

建议:

您在回复正文中使用消息参数

References
In-Reply-To
的做法是正确的,但问题在于您从之前的请求中获得的
messageId
threadId

您也可以使用下面的代码来确保您获得所需线程的

threadId

您只需指定要回复的主题的

subject
即可。下面的代码的行为如下:

  1. 列出收件箱中的线程并使用
    users.threads.list
  2. 收集所有线程 ID
  3. 提取与您要回复的线程的
    subject
    匹配的线程ID
  4. 使用
    users.threads.get
  5. 检索线程详细信息并提取回复所需的元数据
  6. 使用
    users.messages.send
  7. 将回复发送到主题

代码:

"""Authorization and Authentication of Account to Access GMAIL API """
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from email.message import EmailMessage
import base64

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/','https://www.googleapis.com/auth/gmail.modify','https://www.googleapis.com/auth/gmail.modify','https://www.googleapis.com/auth/gmail.modify']
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
    creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        flow = InstalledAppFlow.from_client_secrets_file(
            'credentials.json', SCOPES)
        creds = flow.run_local_server(port=0)
    # Save the credentials for the next run
    with open('token.json', 'w') as token:
        token.write(creds.to_json())

"""Authorization and Authentication of Account to Access GMAIL API """

# Set up the Gmail API client
service = build('gmail', 'v1', credentials=creds)

# Specify the subject to match
subject = 'Sample email thread'

# Retrieve the list of threads
threads = service.users().threads().list(userId='me').execute().get('threads', [])

# Find the thread with the matching subject
matching_thread = None
for thread in threads:
    thread_id = thread['id']
    thread_details = service.users().threads().get(userId='me', id=thread_id).execute()
    message = thread_details['messages'][0]  # Get the first message in the thread
    message_subject = next((header['value'] for header in message['payload']['headers'] if header['name'] == 'Subject'), None)
    if message_subject == subject:
        matching_thread = thread
        break

if matching_thread:
    thread_id = matching_thread['id']
else:
    print("No matching thread found.")

# Retrieve the details of the thread
thread = service.users().threads().get(userId='me', id=thread_id).execute()
messages = thread['messages'][0]['payload']['headers']

# Retrieve the metadata of the thread
for (k) in messages:
    if k['name'] == 'To':
        recipient = k['value']
    if k['name'] == 'Subject':
        email_subject = k['value']
    if k['name'] == 'From':
        sender = k['value']
    if k['name'] == 'Message-ID':
        message_id = k['value']

# Constructing the reply message
message = EmailMessage()
message.set_content('This is a sample reply')
message['To'] = recipient
message['From'] = sender
message['Subject'] = email_subject
message['References '] = message_id
message['In-Reply-To '] = message_id

encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

create_message = {'raw': encoded_message,
                  'threadId': thread_id}
# Sending the reply message to the thread
send_message = (service.users().messages().send(userId="me", body=create_message).execute())

注意:我的授权和身份验证部分的代码可能与您的不同

示例线程

enter image description here

使用代码回复:

enter image description here

参考资料:

Gmail API

用户.线程

方法:users.threads.list

方法:users.threads.get

方法:users.messages.send

© www.soinside.com 2019 - 2024. All rights reserved.