Reputation: 73
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
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
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.
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())
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
.
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.
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.
{
"id": "#####",
"threadId": "#####",
"labelIds": [
"UNREAD",
"SENT",
"INBOX"
]
}
If I misunderstand your question, I'm sorry.
Upvotes: 4