I had a Python script which did this. I had to enable something in the Gmail account. For maybe 3 years the script then ran like this:
import smtplib, ssl
...
subject = 'some subject message'
body = """text body of the email"""
sender_email = 'my_gmail_account_name#gmail.com'
receiver_email = 'some_recipient#something.com'
# Create a multipart message and set headers
message = MIMEMultipart()
message['From'] = 'Mike'
message['To'] = receiver_email
message['Subject'] = subject
# Add body to email
message.attach(MIMEText(body, 'plain'))
# Open file in binary mode
with open( client_zip_filename, 'rb') as attachment:
# Add file as application/octet-stream
# Email client can usually download this automatically as attachment
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
# Encode file in ASCII characters to send by email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
'Content-Disposition',
f'attachment; filename={subject}',
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log in to server using secure context and send email
context = ssl.create_default_context()
with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as server:
print( 'waiting to login...')
server.login(sender_email, password)
print( 'waiting to send...')
server.sendmail(sender_email, receiver_email, text)
print( 'email appears to have been sent')
In May or so of this year I got a message from Google saying that authority to use emails from scripts would be tightened. "Oh dear", I thought.
Some time in June I found that the above script no longer works, and raises an exception, specifically on the line server.login(sender_email, password):
...
File "D:\My documents\software projects\operative\sysadmin_py\src\job_backup_routine\__main__.py", line 307, in main
server.login(sender_email, password)
File "c:\users\mike\appdata\local\programs\python\python39\lib\smtplib.py", line 745, in login
raise last_exception
File "c:\users\mike\appdata\local\programs\python\python39\lib\smtplib.py", line 734, in login
(code, resp) = self.auth(
File "c:\users\mike\appdata\local\programs\python\python39\lib\smtplib.py", line 657, in auth
raise SMTPAuthenticationError(code, resp)
smtplib.SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted.
Learn more at\n5.7.8 https://support.google.com/mail/?p=BadCredentials p14-20020aa7cc8e000000b00435651c4a01sm8910838edt.56 - gsmtp')
... I was thus not entirely surprised by this, and have now gone looking for a solution.
I have got this idea that the way forward is something called "OAuth consent" (I don't have any idea what this is...)
I found this answer and tried to follow the steps there. Here is my account of trying to follow step 1:
I went to this Google configuration page and chose "my_gmail_account_name", the account I want to send emails from ...
new "project", name: test-project-2022-07-18
location: default ("No organisation")
clicked Create
clicked NEXT
clicked ENABLE
clicked the icon to enable the "Google Developer Console"
in the hamburger menu (top left) there is an item "APIs and services" ... one item there is "Credentials" - clicked
one item in the left-hand list is "OAuth consent screen"
another item is "Credentials". Clicked this: then, at the top, "+ CREATE CREDENTIALS"
in the dropdown menu, choose "OAuth Client ID"
clicked "CONFIGURE CONSENT SCREEN"
radio buttons: "Internal" and "External". chose latter.
clicked "CREATE"
under "App information":
"App name": sysadmin_py
"User support email": my_gmail_account_name#gmail.com
"Developer contact information": my_gmail_account_name#gmail.com
clicked "SAVE AND CONTINUE"
then find myself on a page about "SCOPES", with a button "ADD OR REMOVE SCOPES"...
At this point I'm meant to be following "Step 1" instruction "d. Select the application type Other, enter the name "Gmail API Quickstart" and click the Create button"... but nothing of this kind is in view!
The update to that answer was done in 2021-04. A year later the interface in Google appears to have changed radically. Or maybe I have taken the wrong path and disappeared down a rabbit hole.
I have no idea what to do. Can anyone help?
Google has recently made changes to access of less secure apps (read here: https://myaccount.google.com/lesssecureapps).
In order to make your script work again, you'll need to make a new app password for it. Directions to do so are below:
Go to My Account in Gmail and click on Security.
After that, scroll down to choose the Signing into Google option.
Now, click on App Password. (Note: You can see this option when two-step authentication is enabled). To enable two-step authentication:
From the Signing into Google, click on the Two-step Verification option and then enter the password.
Then Turn ON the two-step verification by entering the OTP code received on the mobile.
(Here's a quick link to the same page: https://myaccount.google.com/apppasswords)
Here, you can see a list of applications, choose the required one.
Next, pick the Select Device option and click on the device which is being used to operate Gmail.
Now, click on Generate.
After that, enter the Password shown in the Yellow bar.
Lastly, click on Done.
(Source: https://www.emailsupport.us/blog/gmail-smtp-not-working/)
Simply switch out the password the script is using for this newly generated app password. This worked for me and I wish the same for you.
I hope this helps!
Related
I changed some DocuSign Sample code to add a second recipient during a signing process. With the purpose of the user of my site signing and another (2nd) person hopefully getting an email requesting a further signature of the document. However my code is not working and I don't know if I'm adding the second recipient correctly:
with open(os.path.join(app_path, file_name_path), 'rb') as file:
content_bytes = file.read()
base64_file_content = base64.b64encode(content_bytes).decode('ascii')
document = Document(
document_base64=base64_file_content,
name='Example document',
file_extension='pdf',
document_id=master_id
)
signer = Signer( # this works on its own
email=signer_email,
name=signer_name,
recipient_id='1',
routing_order='1',
client_user_id=client_user_id,
)
signer2 = Signer(
email='secondperson#example.com',
name='Some Guy',
recipient_id='2',
routing_order='2',
client_user_id=client_user_id,
)
sign_here = SignHere(
document_id=str(master_id),
page_number='1',
recipient_id='1',
tab_label='SignHereTab',
x_position='195',
y_position='147')
signer.tabs = Tabs(sign_here_tabs=[sign_here])
envelope_definition = EnvelopeDefinition(
email_subject='Please sign this document sent from the Python SDK',
documents=[document],
recipients=Recipients(signers=[signer, signer2]),
status='sent'
)
api_client = ApiClient()
api_client.host = base_path
api_client.set_default_header('Authorization', 'Bearer ' + access_token)
envelope_api = EnvelopesApi(api_client)
results = envelope_api.create_envelope(account_id,
envelope_definition=envelope_definition)
envelope_id = results.envelope_id
recipient_view_request = RecipientViewRequest(
authentication_method=authentication_method,
client_user_id=client_user_id,
recipient_id='1',
return_url=base_url + '/docusign-return',
user_name=signer_name,
email=signer_email
)
results = envelope_api.create_recipient_view(account_id, envelope_id,
recipient_view_request=
recipient_view_request)
The "secondperson#example.com" address has been added to DocuSign and I activated their DocuSign account. When I run this code the signing process works for the first user. However there is no email sent to "secondperson#example.com" and the document does not appear on the second person's DocuSign "action required". What am I doing wrong?
Update
The document status says "waiting for others" on the DocuSign sandbox, but when I go to the second account there is nothing under "action required".
Several issues that I see:
client_user_id attribute means embedded signer
Since the second signer has the client_user_id attribute set, they are being treated as an embedded signer. As a result they will not receive an email invite to the signing ceremony.
If you want the second signer to be a remote signer (receive an email invite from DocuSign for the signing ceremony), then remove the client_user_id attribute.
Eg
signer2 = Signer(
email='secondperson#example.com',
name='Some Guy',
recipient_id='2',
routing_order='2',
)
Signers do not need DocuSign accounts
In your question you say that
The "secondperson#example.com" address has been added to DocuSign and I activated their DocuSign account.
but (generally speaking) signers do not need DocuSign accounts. (There are some corner cases where they do such as a Part 11 compliance signing.)
Don't use the same client_user_id for multiple signers
Sometimes you do want more than one embedded signer for an envelope. When you do, don't use the same client_user_id for more than one signer. It's bad form. Use each signer's id within your web app. If you don't assign ids in your web app, then use the signer's email as the client user id. If you don't have their email, then use name#example.com.
Added: API authentication for the signing ceremony
Since signers don't pay and don't need a user account on DocuSign, how does a developer's application call EnvelopeViews:createRecipient to obtain the signing ceremony URL that the signer will use?
The answer is to use a "system account" -- create a user within your DocuSign account that generically represents either the application or a department. Eg sales#your_company.com.
Then, create a DocuSign access token by using the JWT grant to impersonate the sales#your_company.com system user.
Your application uses the resulting access token to call the EnvelopeViews:createRecipient to obtain the signing ceremony URL.
Depending on which user sent the envelope, the system account may or may not need administrative privileges.
Looking to create and send messages with multiple files attached. Per the online gmail api documentation, there is a function for building messages with an attachment but no documentation for howto use it to create a message with multiple attachments.
Can I use the gmail API to send messages with multiple attachments programmatically? How might one do this?
With this function, you can send to one or multiple recipient emails, and also you can attach zero, one or more files. Coding improvement recommendations are welcome, however the way it is now, it works.
Python v3.7
smtplib from https://github.com/python/cpython/blob/3.7/Lib/smtplib.py (download the code and create the smtplib.py on your project folder)
def send_email(se_from, se_pwd, se_to, se_subject, se_plain_text='', se_html_text='', se_attachments=[]):
""" Send an email with the specifications in parameters
The following youtube channel helped me a lot to build this function:
https://www.youtube.com/watch?v=JRCJ6RtE3xU
How to Send Emails Using Python - Plain Text, Adding Attachments, HTML Emails, and More
Corey Schafer youtube channel
Input:
se_from : email address that will send the email
se_pwd : password for authentication (uses SMTP.SSL for authentication)
se_to : destination email. For various emails, use ['email1#example.com', 'email2#example.com']
se_subject : email subject line
se_plain_text : body text in plain format, in case html is not supported
se_html_text : body text in html format
se_attachments : list of attachments. For various attachments, use ['path1\file1.ext1', 'path2\file2.ext2', 'path3\file3.ext3']. Follow your OS guidelines for directory paths. Empty list ([]) if no attachments
Returns
-------
se_error_code : returns True if email was successful (still need to incorporate exception handling routines)
"""
import smtplib
from email.message import EmailMessage
# Join email parts following smtp structure
msg = EmailMessage()
msg['From'] = se_from
msg['To'] = se_to
msg['Subject'] = se_subject
msg.set_content(se_plain_text)
# Adds the html text only if there is one
if se_html_text != '':
msg.add_alternative("""{}""".format(se_html_text), subtype='html')
# Checks if there are files to be sent in the email
if len(se_attachments) > 0:
# Goes through every file in files list
for file in se_attachments:
with open(file, 'rb') as f:
file_data = f.read()
file_name = f.name
# Attaches the file to the message. Leaves google to detect the application to open it
msg.add_attachment(file_data, maintype='application', subtype='octet-stream', filename=file_name)
# Sends the email that has been built
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(se_from, se_pwd)
smtp.send_message(msg)
return True
Don't forget to activate less secure apps on your google account (https://myaccount.google.com/lesssecureapps) for this code to work.
Hope this helps
I'm sure this has been asked, but I can't find anything to get mine to work.
I'm trying to send follow up emails to clients, but I want to spoof the email address so the from address is for my coworker. I read somewhere online that the from address in the header is simply a text field that can be edited, but I still cannot send the email.
import smtplib
email_to = '*****#gmail.com'
username = '*******#outlook.com'
password = '*********'
other_email = '*******#outlook.com'
mail = smtplib.SMTP('Outlook.com', 25)
mail.ehlo()
mail.starttls()
mail.login(username,password)
header = ('To:' + email_to + '\n' +'From: ' + other_email + '\n'
+ 'Subject: Python Project Test\n')
message = (header +
'\n\n This is a test message generated from a Python script. \n\n')
mail.sendmail(username, email_to, message)
mail.close()
print("Email sent successfully.")
I know this can be done, but can someone point me in the right direction? Is there any way for me to disguise my name in the from field as that of the email that is supposed to get this?
===================================
Also, for the sake of completion, here is the error I got:
Traceback (most recent call last):
File "C:\Users\*****\Desktop\email outlook.py", line 16, in <module>
mail.sendmail(username, email_to, message)
File "C:\Users\*****\AppData\Local\Programs\Python\Python36-32\lib\smtplib.py", line 887, in sendmail
raise SMTPDataError(code, resp)
smtplib.SMTPDataError: (550, b'5.7.60 SMTP; Client does not have permissions to send as this sender')
I was hoping if there was a way to make the other_name an alias of the username.
The very short version: This isn't going to work.
Once upon a time, it was reasonably possible to do what you are asking to do. In the old days, when the internet was small and spam did not exist, the receiving server would just trust you. You could just connect to mail.example.com and say you were sending on behalf of someone#example.org, and example.com would just believe you.
But those days are over and done with. Nowadays, SMTP servers are a lot less trusting. So let's go through the problems with your approach:
You are trying to route your email through outlook.com. outlook.com knows perfectly well that you are username and not other_email. If you want to send email as other_email, you need to authenticate as other_email.
You could connect directly to gmail.com, claim to be outlook.com, and try to send the email that way. But Gmail knows you're not outlook.com, because you're missing these things. So it will likely flag your message as spam, bounce it, or even* accept it and then discard it entirely.
You could fix (1) by changing your code, but because of (2), there's little point.
* I do not work on the Gmail team. I am guessing how Gmail would respond to this based solely on public information about how modern email servers are typically configured. YMMV.
I am trying to send a simple mail with python
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login("mymail#gmail.com", "mypassword")
msg = "Hello world"
server.sendmail("mymail#gmail.com", "mymail#gmail.com", msg)
server.quit()
But I get this err:
server.login("user#gmail.com", "psw")
File "C:\Python\lib\smtplib.py", line 652, in login
raise SMTPAuthenticationError(code, resp)
smtplib.SMTPAuthenticationError: (534, b'5.7.14 <https://accounts.google.com/ContinueSignIn?sarp=1&scc=1&plt=AKgnsbuxb\n5.7.14 4i2u8qU8V3jgf6uGv8da1RAGPJyctRvIFy_kjai6aKVx_B6qVhoz_dzFpvfPC18H-jeM6K\n5.7.14 cnm2HVuq-wr-uw59hD31ms-cxMmnZuq6Z3_liDaDmu8_UqaiUwR4FUiuX2i5pPdQjJzFvv\n5.7.14 4VrEF5XT4ol2iN17gnB_jITpwzsjH9Ox3NCNcfl7SriHr5m7esc15PWI0CG_2CTlyh7RxW\n5.7.14 XhoJPajs8GMd-khOQWUqucywfrfo> Please log in via your web browser and\n5.7.14 then try again.\n5.7.14 Learn more at\n5.7.14 https://support.google.com/mail/bin/answer.py?answer=78754 ef10sm13614207wjd.49 - gsmtp')
What should I do?
Thanks
It seems as if you require something that Google calls an app password.
Basically, you generate a 16 digit password, which is unique to your app. You enter this specific password in the python program, instead of the password you regularly use to log into your Google account.
This allows you to still enjoy the benefits of 2-step verification while also being able to use third party applications, such as your own python program.
Here are the instructions from Google on how to generate such an app password:
https://support.google.com/accounts/answer/185833?hl=en
you can use this code:
import smtplib
session = smtplib.SMTP('smtp.gmail.com', 587)
session.ehlo()
session.starttls()
session.login('youremail#gmail.com',' password')
headers = "\r\n".join(["from: " + 'youremail#gmail.com',
"subject: " + "test",
"to: " + 'contactemail#gmail.com',
"mime-version: 1.0",
"content-type: text/html"])
# body_of_email can be plaintext or html!
content = headers + "\r\n\r\n" + "body_of_email"
session.sendmail('youremail#gmail.com', 'contactemail#gmail.com', content)
just remember if your email is gmail after first run you get an error. after that you should login to your email account and approve access to your account from another app ( you will receive a messege after login)
You could use a free mail API such as mailgun:
import requests
def send_simple_message(target):
return requests.post(
"https://api.mailgun.net/v3/samples.mailgun.org/messages",
auth=("api", "key-3ax6xnjp29jd6fds4gc373sgvjxteol0"),
data={"from": "Excited User <excited#samples.mailgun.org>",
"to": [target],
"subject": "Hello",
"text": "Testing some Mailgun awesomeness!"})
send_simple_message('target#email.com')
Using an API like this avoids the issue of individual account authentication all together.
See also: This question for info on using smtplib
Yea, like the answer posted, it was a matter of authentication :)
I'd like to further help you with sending emails by advising the yagmail package (I'm the maintainer, sorry for the advertising, but I feel it can really help!). Note that I'm also maintaining a list of common errors there, such as the authentication error.
The whole code for you would be:
import yagmail
yag = yagmail.SMTP('user', 'pw')
yag.send(contents = msg)
Note that I provide defaults for all arguments, for example if you want to send to yourself, you can omit "to = myemail#gmail.com", if you don't want a subject, you can omit it also.
Furthermore, the goal is also to make it really easy to attach html code or images (and other files).
Where you put contents you can do something like:
contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
'You can also find an audio file attached.', '/local/path/song.mp3']
Wow, how easy it is to send attachments! This would take like 20 lines without yagmail ;)
Also, if you set it up once, you'll never have to enter the password again (and have it safely stored). In your case you can do something like:
import yagmail
yagmail.SMTP().send(contents = contents)
which is much more concise!
I'd invite you to have a look at the github or install it directly with pip install yagmail.
I am writing a Python program that can login Gmail.
The purpose of this program is to check whether the username/password combination exists and is correct.
Since this program is to test the the username/password combination existence, it's no need to know any mail contents in Gmail.
The input of this program is a username and password.
The output of this program is either
successful login
or
login failure
Login failure could be:
existing username+wrong password
nonexisting username
My idea is to login Gmail first. Afterward, when login failure, the gmail webpage will show particular message on the login webpage. I can parse the webpage content and check whether it has that particular message.
However, I still have no idea how to login Gmail in Python. Please let me know which module can be used or write me a small piece of sample code.
Here's an idea:
Why don't you try to send an email from the account and see if it sends? You can do this with smtplib in the python standard module. There's code examples here. You'll have to look into the doc of the module, but it looks like an exception is thrown if the login fails, which should contain the details you're interested in.
In edit:
I dug up this bit of code that I wrote to do exactly that. You'll need to put a try/catch around the bit at the bottom to detect erroneous login credentials.
# Subject
now = dt.datetime.now().ctime()
subject = 'Change to system on %s' % now
# Body
body = 'Subject: %s,\n' % subject
body += 'On %s, a change to the system was detected. Details follow.\n\n' % now
relevantFiles = list(set([x.file for x in relevantChunks]))
for file in relevantFiles:
fileChunks = [x for x in relevantChunks if x.file == file]
for chunk in fileChunks:
body += '****** Affected file %s. ' % chunk.file
<some other stuff>
server = smtp.SMTP(args.host) # host = smtp.gmail.com:<port> look this bit up
server.starttls()
server.login(args.username, args.password)
server.sendmail(args.sender, args.recipient, body)
server.quit()
As an aside, I'm not quite sure why this question was down-voted, or even what it takes to be down-voted other than the fact that you asked the wrong question.
try this:
from email.mime.text import MIMEText
import smtplib
msg = MIMEText("Hello There!")
msg['Subject'] = 'A Test Message'
msg['From'] = 'username#gmail.com'
msg['To'] = 'username#gmail.com'
s = smtplib.SMTP('smtp.gmail.com:587')
s.starttls() ##Must start TLS session to port 587 on the gmail server
s.login('username', 'passsword') ##Must pass args gmail username & password in quotes to authenticate on gmail
s.sendmail('username#gmail.com',['username#gmail.com'],msg.as_string())
print("Message Sent")
This kind of things are like prohibited, that's why things like OAuth or OpenID are created. This kind of things permit the user to login without entering username and password. So be careful.