im trying to send a gmail message with the following code:
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64
def create_message(sender, to, subject, message_text):
"""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_text: The text of the email message.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
#return {'raw': base64.urlsafe_b64encode(message.as_string())}
b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
b64_string = b64_bytes.decode()
#body = {'raw': b64_string}
return {'raw': b64_string}
s = "email"
t = "email"
sub = "test subject"
m = "message test"
create_message(s,t,sub,m)
where s = my email(replaced with "email" in this example and t = my other email (again replaced))
It doesnt kick out any errors but doesnt send the message.
Also this code didnt work:
#return {'raw': base64.urlsafe_b64encode(message.as_string())}
as it produced the following error: TypeError: a bytes-like object is required, not 'str'
"I solved that" with this:
b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
b64_string = b64_bytes.decode()
#body = {'raw': b64_string}
return {'raw': b64_string}
edit(1): I do have credentials.json and passed the google api example
Needed to create client_secret.json and paste my credentials there, ctrc+c, ctrl+v an old example and update it to python 3.x, resolved base 64 issue and it works :)
import httplib2
import os
from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools
from oauth2client import file
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
SCOPES = 'https://mail.google.com/'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Quickstart'
def get_credentials():
"""Gets valid user credentials from storage.
If nothing has been stored, or if the stored credentials are invalid,
the OAuth2 flow is completed to obtain the new credentials.
Returns:
Credentials, the obtained credential.
"""
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'gmail-quickstart.json')
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatability with Python 2.6
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
import base64
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 mimetypes
from httplib2 import Http
from apiclient import errors
from apiclient.discovery import build
credentials = get_credentials()
service = build('gmail', 'v1', http=credentials.authorize(Http()))
def SendMessage(service, user_id, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
message: Message to be sent.
Returns:
Sent Message.
"""
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print('Message Id: %s' % message['id'])
return message
except errors.HttpError as error:
print('An error occurred: %s' % error)
def CreateMessage(sender, to, subject, message_text):
"""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_text: The text of the email message.
Returns:
An object containing a base64 encoded email object.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
b64_string = b64_bytes.decode()
#body = {'raw': b64_string}
return {'raw': b64_string}
#return {'raw': base64.b64encode(message.as_string())}
testMessage = CreateMessage("email","email","test_subject","test_message")
testSend = SendMessage(service, 'me', testMessage)
emails replaced with email in line 106 (second from bottom)
Related
I have a python script that detects motion in teh camera and then sends an email to the supplied email.
The issue is that Gmail has changed the way it allows apps to send external emails.
How can I change my code to properly send an email with an attached image?
Here is my code:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
# Email you want to send the update from (only works with gmail)
fromEmail = 'some_email#gmail.com'
# You can generate an app password here to avoid storing your password in plain text
# https://support.google.com/accounts/answer/185833?hl=en
fromEmailPassword = 'somepassword'
# Email you want to send the update to
toEmail = 'some_email#gmail.com'
smtp_port = 587 # Standard secure SMTP port
smtp_server = "smtp.gmail.com" # Google SMTP Server
TIE_server = smtplib.SMTP(smtp_server, smtp_port)
def sendEmail(image):
msgRoot = MIMEMultipart('related')
msgRoot['Subject'] = 'Security Update'
msgRoot['From'] = fromEmail
msgRoot['To'] = toEmail
msgRoot.preamble = 'camera update'
msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)
msgText = MIMEText('Smart security cam found object')
msgAlternative.attach(msgText)
msgText = MIMEText('<img src="cid:image1">', 'html')
msgAlternative.attach(msgText)
msgImage = MIMEImage(image)
msgImage.add_header('Content-ID', '<image1>')
msgRoot.attach(msgImage)
zsmtp = smtplib.SMTP('smtp.gmail.com', 587)
smtp.starttls()
smtp.login(fromEmail, fromEmailPassword)
smtp.sendmail(fromEmail, toEmail, msgRoot.as_string())
smtp.quit()
Thanks.
When I send files with the same subject using the code the emails accumulate in the inbox.
What I want to do is that these do not accumulate and are sent one by one with the same subject.
from email.mime.text import MIMEText
import smtplib
import smtplib, ssl
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
receiver = 'receiver'
sender = 'sender'
senders_password = 'password_application_sender'#This password is a password of application, this option activate in manager account of his gmail account
file = "path of file"
subject = 'subject' #This subject always is the same
mensaje = MIMEMultipart("alternative")
mensaje["Subject"] = subject
mensaje["From"] = sender
mensaje["To"] = receiver
html = f"""
<html>
<body>
Hi this is a email with a attachment
</body>
</html>
"""
body_html = MIMEText(html, "html")
mensaje.attach(body_html)
with open(file, "rb") as attachment:
content = MIMEBase("application","octet-stream")
content.set_payload(attachment.read())
encoders.encode_base64(content)
content.add_header(
"Content-Disposition",
f"attachment; filename= {file}",
)
mensaje.attach(content)
text = mensaje.as_string()
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
server.login(sender, senders_password)
server.sendmail(sender, receiver, text)
server.quit()
print("Email Send")
How I receive the email and How should it get
I am using this script to send email using AWS SES and I am able to receive email in my inbox but I am not able to see X-SES-CONFIGURATION-SET header in the received email. I also tried using Simple email body type with ConfigurationSetName still no luck. Any help is highly appreciated.
import boto3
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Replace sender#example.com with your "From" address.
# This address must be verified with Amazon SES.
SENDER = "Niranj Raja <niranj#xxx.com>"
# Replace recipient#example.com with a "To" address. If your account
# is still in the sandbox, this address must be verified.
RECIPIENT = "niranj#xxx.com"
# Specify a configuration set. If you do not want to use a configuration
# set, comment the following variable, and the
# ConfigurationSetName=CONFIGURATION_SET argument below.
CONFIGURATION_SET = "test-me"
# The subject line for the email.
SUBJECT = "Amazon SES Test (SDK for Python)"
# The email body for recipients with non-HTML email clients.
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto)."
)
# The HTML body of the email.
BODY_HTML = """<html>
<head></head>
<body>
<h1>Amazon SES Test (SDK for Python)</h1>
<p>This email was sent with
<a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
<a href='https://aws.amazon.com/sdk-for-python/'>
AWS SDK for Python (Boto)</a>.</p>
</body>
</html>
"""
# The character encoding for the email.
CHARSET = "UTF-8"
msg = MIMEMultipart('mixed')
# Add subject, from and to lines.
msg['Subject'] = SUBJECT
msg['From'] = SENDER
msg['To'] = RECIPIENT
#msg['X-SES-CONFIGURATION-SET'] = CONFIGURATION_SET
msg.add_header('X-SES-CONFIGURATION-SET', CONFIGURATION_SET)
print(dir(msg))
print('')
# Create a multipart/alternative child container.
msg_body = MIMEMultipart('alternative')
print(dir(msg_body))
# Encode the text and HTML content and set the character encoding. This step is
# necessary if you're sending a message with characters outside the ASCII range.
textpart = MIMEText(BODY_TEXT.encode(CHARSET), 'plain', CHARSET)
htmlpart = MIMEText(BODY_HTML.encode(CHARSET), 'html', CHARSET)
msg_body.attach(textpart)
msg_body.attach(htmlpart)
# Create a new SES resource and specify a region.
client = boto3.client(
'sesv2',
aws_access_key_id='test',
aws_secret_access_key='test1',
region_name='region-10'
)
# Try to send the email.
try:
# Provide the contents of the email.
response = client.send_email(
FromEmailAddress=SENDER,
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Content={
'Raw': {
'Data': msg_body.as_string()
}
}
)
print(response)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
I think there is an issue with Gmail API and Python3.
The original code in the documentation is in Python2, but for several reasons, including that my application is already working with Python3, I was passing the code to python3.
So... after sorting several issues, including a 400 request error, (which was apparently that the auth I provided to google wasn't corretly done) I'm facing (I hope) the final issue that apparently I'm trying to read a file but
Even just doing token.read() generates the same issue.
The token.pickle file is autogenerated once you've authorized google to access your email account and that the app can send emails automatically.
I know the credentials.json file is correct, as that's the point to tell google who you are and it always is reading my credentials correctly, redirecting me to give authorization to my app.
Here's the app to send emails, I think it's pretty straightforward, I followed the documentation and looked at other issues like this one and finally got this far:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.errors import HttpError
# from httplib2 import Http
# from oauth2client import client, tools, file
import base64
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 mimetypes
from apiclient import errors
SCOPES = 'https://mail.google.com/'
def SendMessage(service, user_id, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
message: Message to be sent.
Returns:
Sent Message.
"""
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print(f'Message Id: {message["id"]}')
return message
except errors.HttpError as error:
print(f'An error occurred: {error}')
def CreateMessage(sender, to, subject, message_text):
"""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_text: The text of the email message.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
message_bytes = message.as_string().encode('utf-8')
# return { 'raw': base64.urlsafe_b64encode(message.as_string()) }
# return { 'raw': base64.urlsafe_b64encode(message_bytes) }
raw = base64.urlsafe_b64encode(message_bytes)
return raw.decode()
def main():
creds = None
# The file token.pickle 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.pickle'):
with open('token.pickle', 'r') as token:
creds = pickle.load(token)
# 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.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
# Create
message = CreateMessage('my-email#gmail.com', 'destination#gmail.com', 'Testing Gmail API', 'Hi GMAIL API')
# Send
SendMessage(service, "me", message)
if __name__ == '__main__':
main()
I truly don't know what to do here, if someone has already solved this issue and how.
THANKS!
In your script, when token.pickle is not created, Gmail API can be used by creating token.pickle at 1st authorization. But an error of 'raw' RFC822 payload message string or uploading message via /upload/* URL required occurs at message = (service.users().messages().send(userId=user_id, body=message).execute()). So in this case, please modify as follows.
From:
raw = base64.urlsafe_b64encode(message_bytes)
return raw.decode()
To:
raw = base64.urlsafe_b64encode(message_bytes).decode('utf-8')
return {'raw': raw}
After above modification, when your script is run as the 2nd run, when token.pickle, which has already been created, is read, an error occurs. I think that this error is in your title. In this case, please modify as follows.
From:
with open('token.pickle', 'r') as token:
To:
with open('token.pickle', 'rb') as token:
By this, I think that the script works.
By the way, if an error occurs when the code is retrieved, also please modify as follows.
From:
creds = flow.run_local_server(port=0)
To:
creds = flow.run_local_server()
Note:
About the authorization, you can see the sample script at the Quickstart of the official document.
If this was not the direction of your issue, I apologize.
import httplib2
import os
import oauth2client
from oauth2client import client, tools
from apiclient import errors, discovery
SCOPES = 'https://mail.google.com/'
APPLICATION_NAME = 'Gmail API Python List Email'
CLIENT_SECRET_FILE = 'client_secret_ishandutta2007.json'# This file will be in local dir
CREDENTIAL_FILE_NAME = 'gmail-python-email-send_ishandutta2007.json'
def get_credentials():
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir, CREDENTIAL_FILE_NAME)
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
credentials = tools.run_flow(flow, store)
print(('Storing credentials to ' + credential_path))
return credentials
def ListMessagesMatchingQuery(service, user_id, query=''):
try:
response = service.users().messages().list(userId=user_id, q=query).execute()
messages = []
if 'messages' in response:
messages.extend(response['messages'])
while 'nextPageToken' in response:
page_token = response['nextPageToken']
response = service.users().messages().list(userId=user_id, q=query,
pageToken=page_token).execute()
messages.extend(response['messages'])
return messages
except errors.HttpError as error:
print('An error occurred: %s' % error)
def main():
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
ListMessagesMatchingQuery(service, "me", query="senior recruit")
if __name__ == '__main__':
main()
An error occurred: https://www.googleapis.com/gmail/v1/users/me/messages?q=senior+recruit&alt=json
returned "Insufficient Permission">
Found the issues,in the code above it was picking up a cached credentials from the file gmail-python-email-send_ishandutta2007.json which has permission to send mails only, changing this filename to something new fixed the issue.