我创建了两个 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())
由于这部分代码,答案可以正确显示(对于答案的发送者),如上所述,但对于接收者来说,它显示为一条新消息 - 未链接到线程。
其实我找到了为什么我的方法不起作用;即使字典提到了一种消息 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
,并且线程已成功创建。
您需要将回复消息作为主题的一部分发送。您需要从原始消息中识别线程 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。
您在回复正文中使用消息参数
References
和 In-Reply-To
的做法是正确的,但问题在于您从之前的请求中获得的 messageId
和 threadId
。
您也可以使用下面的代码来确保您获得所需线程的
threadId
。
您只需指定要回复的主题的
subject
即可。下面的代码的行为如下:
users.threads.list
subject
匹配的线程IDusers.threads.get
users.messages.send
"""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())
注意:我的授权和身份验证部分的代码可能与您的不同