Search code examples
pythongoogle-apigmailgmail-apigoogle-api-python-client

Drafting a reply with gmail API doesn't attach draft to thread


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

Solution

  • 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}')