hirano00o
hirano00o

Reputation: 73

400 Error. Recipient address required. curl

I used Gmail API with curl.( Users.messages: send)

But I recieve Error 400 recipient address required.

Command

curl -X POST -H "Authorization: Bearer *****" -H "Content-Type:message/rfc822" -d "{'raw':'Encoded Value'}" "https://www.googleapis.com/upload/gmail/v1/users/me/messages/send"

Response

{
   "error": {
     "errors": [
        {
         "domain": "global",
         "reason": "invalidArgument",
         "message": "Recipient address required"
       }
     ],
     "code": 400,
     "message": "Recipient address required"
   }
 }


The encoded value was created by the following python script.

import base64
from email.mime.text import MIMEText
from email.utils import formatdate

MAIL_FROM = "[email protected]"
MAIL_TO = "[email protected]"

def create_message():
    message = MIMEText("Gmail body: Hello world!")
    message["from"] = MAIL_FROM
    message["to"] = MAIL_TO
    message["subject"] = "gmail api test"
    message["Date"] = formatdate(localtime=True)

    byte_msg = message.as_string().encode(encoding="UTF-8")
    byte_msg_b64encoded = base64.urlsafe_b64encode(byte_msg)
    str_msg_b64encoded = byte_msg_b64encoded.decode(encoding="UTF-8")

    return {"raw": str_msg_b64encoded}

print(create_message())

Upvotes: 2

Views: 2135

Answers (2)

Umar Kayondo
Umar Kayondo

Reputation: 724

I would like to give my thanks to the other answers which were helpful. My use case required me to use the user's access token obtained from Gmail and respond back to a given message thread.

In this, I had to modify the above answer given by Tanaike to the following.

import base64
import mimetypes
import os
import traceback
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

import httplib2
from apiclient import discovery
from oauth2client.client import AccessTokenCredentials


def send_message(access_token: str, sender: str, to: str, subject: str, html_message: str, thread_id: str,
                 user_id: str = 'me', attachment_file=None):
    try:
        # Referenced from: https://oauth2client.readthedocs.io/en/latest/source/oauth2client.client.html
        credentials = AccessTokenCredentials(access_token, 'PostmanRuntime/7.29.2')
        http = credentials.authorize(httplib2.Http())
        service = discovery.build('gmail', 'v1', http=http)
        if attachment_file:
            message_body = __create_message_with_attachment__(sender, to, subject, html_message, thread_id, attachment_file)
        else:
            message_body = __create_message_body__(sender, to, subject, html_message, thread_id)
        response = (service.users().messages().send(userId=user_id, body=message_body).execute())
        print(response)
        return response['id']
    except Exception as e:
        traceback.print_exc()


def __create_message_body__(from_email: str, to_email: str, subject: str, message_body: str, thread_id: str):
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = to_email
    msg.attach(MIMEText(message_body, 'plain'))
    msg.attach(MIMEText(message_body, 'html'))
    return {
        'raw': base64.urlsafe_b64encode(msg.as_string().encode()).decode(),
        'threadId': thread_id
    }


def __create_message_with_attachment__(sender: str, to: str, subject: str, message_body, thread_id: str,
                                   attachment_file: str):
    """Create a message for an email.

    Args:
      sender: Email address of the sender.
      to: Email address of the receiver.
      subject: The subject of the email message.
      message_body: Html message to be sent
      thread_id: thread id to respond to
      attachment_file: The path to the file to be attached.

    Returns:
      An object containing a base64url encoded email object.
    """
    message = MIMEMultipart('mixed')
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject

    message_alternative = MIMEMultipart('alternative')
    message_related = MIMEMultipart('related')

    message_related.attach(MIMEText(message_body, 'html'))
    message_alternative.attach(MIMEText(message_body, 'plain'))
    message_alternative.attach(message_related)

    message.attach(message_alternative)

    print("create_message_with_attachment: file: %s" % attachment_file)
    content_type, encoding = mimetypes.guess_type(attachment_file)

    if content_type is None or encoding is not None:
        content_type = 'application/octet-stream'
    main_type, sub_type = content_type.split('/', 1)
    if main_type == 'text':
        fp = open(attachment_file, 'rb')
        msg = MIMEText(fp.read(), _subtype=sub_type)
        fp.close()
    elif main_type == 'image':
        fp = open(attachment_file, 'rb')
        msg = MIMEImage(fp.read(), _subtype=sub_type)
        fp.close()
    elif main_type == 'audio':
        fp = open(attachment_file, 'rb')
        msg = MIMEAudio(fp.read(), _subtype=sub_type)
        fp.close()
    else:
        fp = open(attachment_file, 'rb')
        msg = MIMEBase(main_type, sub_type)
        msg.set_payload(fp.read())
        fp.close()
    filename = os.path.basename(attachment_file)
    msg.add_header('Content-Disposition', 'attachment', filename=filename)
    message.attach(msg)
    return {
        'raw': base64.urlsafe_b64encode(message.as_string().encode()).decode(),
        'threadId': thread_id
    }


def main():
    to = "[email protected]"
    sender = "[email protected]"
    subject = "subject"
    message = "Hi<br/>same message here"
    access_token = "ya29.a0AVA9y1uc1Ec-................"
    thread_id = '181b9292e6a.....'

    send_message(access_token=access_token,
                 sender=sender,
                 to=to,
                 subject=subject,
                 html_message=message,
                 thread_id=thread_id)


if __name__ == '__main__':
    main()

Please keep note of how I construct the required credential using the user's access token;

credentials = AccessTokenCredentials(access_token, 'user agent here')

This is obtained from https://oauth2client.readthedocs.io/en/latest/source/oauth2client.client.html

Upvotes: 0

Tanaike
Tanaike

Reputation: 201378

When the messages are sent by the media upload requests using https://www.googleapis.com/upload/gmail/v1/users/me/messages/send, the request body is required to be created as follows. I modified your python script for creating the request body. Please confirm it.

Modified python script :

import base64
from email.mime.text import MIMEText
from email.utils import formatdate

MAIL_FROM = "[email protected]"
MAIL_TO = "[email protected]"


def encode(v):
    byte_msg = v.encode(encoding="UTF-8")
    byte_msg_b64encoded = base64.b64encode(byte_msg)
    return byte_msg_b64encoded.decode(encoding="UTF-8")


def create_message():
    message = "To: " + MAIL_TO + "\n"
    message += "From: " + MAIL_FROM + "\n"
    message += "Subject: =?utf-8?B?" + encode("gmail api test") + "?=\n"
    message += "Date: " + formatdate(localtime=True) + "\n"
    message += "Content-Type: multipart/alternative; boundary=boundaryboundary\n\n"
    message += "--boundaryboundary\n"
    message += "Content-Type: text/plain; charset=UTF-8\n"
    message += "Content-Transfer-Encoding: base64\n\n"
    message += encode("Hello world!") + "\n\n"
    message += "--boundaryboundary"
    return message


print(create_message())

Result :

To: [email protected]
From: [email protected]
Subject: =?utf-8?B?Z21haWwgYXBpIHRlc3Q=?=
Date: Thu, 15 Mar 2018 01:23:45 +0100
Content-Type: multipart/alternative; boundary=boundaryboundary

--boundaryboundary
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64

SGVsbG8gd29ybGQh

--boundaryboundary

Please save above request body to a file as a text file. As a sample, the filename is sample.txt.

Important point :

Here, please be careful the place of "EOF" of the file. Please don't break after the last --boundaryboundary. If it breaks after the last --boundaryboundary, the body is not received. The image is as follows.

enter image description here

Curl command :

curl -s -X POST \
  -H "Authorization: Bearer *****" \
  -H "Content-Type: message/rfc822" \
  --data-binary "@sample.txt" \
  "https://www.googleapis.com/upload/gmail/v1/users/me/messages/send"

It posts sample.txt as the binary data.

Result :

{
 "id": "#####",
 "threadId": "#####",
 "labelIds": [
  "UNREAD",
  "SENT",
  "INBOX"
 ]
}

Note :

  • This is a very simple sample, so please modify this to your environment.
  • This answer supposes that your access token can be used for this situation. If the error related to the access token occurs, please check the scopes.

If I misunderstand your question, I'm sorry.

Upvotes: 4

Related Questions