0

I want to implement functionality to append a draft as a reply to an existing thread of emails using the gmail API in Python, but my drafts intended to be replies to a thread are created as standalone drafts. The docs for the create() method are here and the docs for the RFC 2822 standard are here. I believe I have already correctly set the Subject header to match the Subject header of the message being replied to, the In-Reply-To header to match the Message-ID header of the message being replied to, and the References header to match the References header of the message being replied to followed by the Message-ID (separated with a space).

My code for getting the headers from the message being replied to is in get_message_headers() and my code for setting the reply's headers and creating the draft is in reply_draft().

In the main script, I call get_message_headers() and then draft the reply with reply_draft(), inputting the values returned by get_message_headers() as well as the body of the new draft, the target's email address, and the thread ID of the message being replied to.

Does anyone know what I am missing to get the draft to be created as a reply to a thread instead of a new draft?

    def get_message_headers(self, message_id):
        """Get a message and return its References, In-Reply-To, and Subject headers"""
        payload = (
            self.service.users()
            .messages()
            .get(userId="me", id=message_id)
            .execute()["payload"]
        )
        references_value = None
        in_reply_to_value = None
        subject = None

        for header in payload["headers"]:
            if header["name"] == "References":
                refs = header["value"]
            elif header["name"] == "Message-ID":
                in_reply_to_value = header["value"]
            elif header["name"] == "Subject":
                subject = header["value"]
        references_value = refs + " " + in_reply_to_value
        return references_value, in_reply_to_value, subject

    def reply_draft(
        self, content, other, subject, thread_id, references_value, in_reply_to_value
    ):
        """Create and insert a draft email.
        Print the returned draft's message and id.
        Returns: Draft object, including draft id and message meta data.
        """
        try:
            message = EmailMessage()

            message.set_content(content)

            message["To"] = other
            message["From"] = self.me
            message["Subject"] = subject
            message["References"] = references_value
            message["In-Reply-To"] = in_reply_to_value

            # encoded message
            encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
            body = {"message": {"raw": encoded_message}}
            body["threadId"] = str(thread_id)

            draft = (
                self.service.users().drafts().create(userId="me", body=body).execute()
            )

        except HttpError as error:
            print(f"An error occurred: {error}")
            draft = None

        return draft
3
  • I have to apologize for my poor English skill. Unfortunately, I cannot understand the relationship between get_message_headers and reply_draft in your showing script. Can you provide a sample script for correctly replicating your current issue? First, I would like to correctly understand your question. Commented Nov 13, 2023 at 8:11
  • Yes @Tanaike I first call get_message_headers() and then call reply_draft() using the values returned by get_message_headers() Commented Nov 13, 2023 at 16:45
  • Thank you for replying. Now, I noticed that an answer has already been posted. In this case, I would like to respect the existing answer and discussion. Commented Nov 14, 2023 at 0:08

1 Answer 1

2

If I understand your question correctly you have a message and you want to create a draft of a reply to that message.

You just need to add the threadId with the id of the message you want to reply to.

try:
    # Call the Gmail API
    service = build('gmail', 'v1', credentials=creds)
    message = EmailMessage()

    message.set_content('This is automated draft mail')

    message['To'] = '[redacted]'
    message['From'] = [redacted]
    message['Subject'] = 'Automated draft'

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

    create_message = {
        'message': {
            'threadId': message_id,   # The id of the main message to reply to
            'raw': encoded_message
        }
    }
    # pylint: disable=E1101
    draft = service.users().drafts().create(userId="me",
                                            body=create_message).execute()

    print(F'Draft id: {draft["id"]}\nDraft message: {draft["message"]}')

except HttpError as error:
    # TODO(developer) - Handle errors from gmail API.
    print(f'An error occurred: {error}')
Sign up to request clarification or add additional context in comments.

5 Comments

Hi Linda, thanks for the response. You are understanding my question correctly. However, I'm already setting the threadID field (body["threadId"] = str(thread_id) in reply_draft()), and the requirements for creating a reply also include setting the headers I mentioned above, which I think may be the root of my problem but I'm not sure what is missing, as I seem to be already filling in everything per the requirements in RFC 2822.
Did you try my code? I ran the code above and it works. The client library handles setting the headers for you. Check your body compared to mine. Your setting it wrong.
this is embarrassing... no I hadn't tried your code, just assumed that what I was doing was equivalent. And I tried it (only change I made is setting threadId the way you did) and it worked! Thanks so much. Only comment is that each message has an id and a threadId. Your comment says to use the "id of the main message to reply to", but I used the threadId of the main message to reply to, and that worked. Thanks very much for your help!
Anytime im glad to here you got it working in the end :)
Just under a year later and want to follow up with a massive "Thank you" - really thought I was going crazy...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.