How To Set Up A Boring Bar
Sending Electronic mail and Text Messages
Checking and replying to e-mail is a huge time sink. Of course, yous can't just write a program to handle all your email for you, since each message requires its own response. But you tin still automate plenty of email-related tasks one time you know how to write programs that can send and receive email.
For example, maybe you have a spreadsheet full of client records and want to send each customer a different form letter depending on their historic period and location details. Commercial software might not be able to do this for you; fortunately, you can write your own program to send these emails, saving yourself a lot of time copying and pasting form emails.
You can besides write programs to transport emails and SMS texts to notify y'all of things even while yous're away from your computer. If you're automating a task that takes a couple of hours to do, you lot don't want to go back to your figurer every few minutes to bank check on the program's status. Instead, the program can just text your phone when it's washed—freeing you lot to focus on more of import things while y'all're away from your calculator.
SMTP
Much similar HTTP is the protocol used by computers to send web pages across the Internet, Unproblematic Postal service Transfer Protocol (SMTP) is the protocol used for sending email. SMTP dictates how e-mail messages should be formatted, encrypted, and relayed between mail servers, and all the other details that your computer handles after you click Send. You lot don't need to know these technical details, though, because Python's smtplib
module simplifies them into a few functions.
SMTP just deals with sending emails to others. A dissimilar protocol, chosen IMAP, deals with retrieving emails sent to you and is described in IMAP.
Sending Email
You may be familiar with sending emails from Outlook or Thunderbird or through a website such as Gmail or Yahoo! Mail. Unfortunately, Python doesn't offer you a nice graphical user interface like those services. Instead, you lot call functions to perform each major footstep of SMTP, as shown in the following interactive shell case.
>>> import smtplib >>> smtpObj = smtplib.SMTP('smtp.example.com', 587) >>> smtpObj.ehlo() (250, b'mx.example.com at your service, [216.172.148.131]\nSIZE 35882577\ n8BITMIME\nSTARTTLS\nENHANCEDSTATUSCODES\nCHUNKING') >>> smtpObj.starttls() (220, b'2.0.0 Ready to start TLS') >>> smtpObj.login('bob@example.com', ' MY_SECRET_PASSWORD ') (235, b'2.seven.0 Accepted') >>> smtpObj.sendmail('bob@case.com', 'alice@example.com', 'Subject: Then long.\nDear Alice, so long and thanks for all the fish. Sincerely, Bob') {} >>> smtpObj.quit() (221, b'2.0.0 closing connection ko10sm23097611pbd.52 - gsmtp')
In the following sections, nosotros'll go through each step, replacing the placeholders with your information to connect and log in to an SMTP server, ship an email, and disconnect from the server.
Connecting to an SMTP Server
If you lot've ever prepare Thunderbird, Outlook, or another plan to connect to your email account, yous may exist familiar with configuring the SMTP server and port. These settings will exist different for each electronic mail provider, merely a web search for <your provider> smtp settings should turn up the server and port to apply.
The domain name for the SMTP server will commonly be the name of your email provider's domain proper noun, with smtp. in front of it. For case, Gmail's SMTP server is at smtp.gmail.com . Table 16-one lists some mutual email providers and their SMTP servers. (The port is an integer value and will almost always be 587, which is used by the command encryption standard, TLS.)
Table sixteen-one. Email Providers and Their SMTP Servers
Provider | SMTP server domain proper name |
---|---|
Gmail | smtp.gmail.com |
Outlook.com/Hotmail.com | smtp-mail.outlook.com |
Yahoo Postal service | smtp.mail.yahoo.com |
AT&T | smpt.postal service.att.net (port 465) |
Comcast | smtp.comcast.net |
Verizon | smtp.verizon.net (port 465) |
One time you lot take the domain name and port information for your email provider, create an SMTP
object by calling smptlib.SMTP()
, passing the domain name every bit a string statement, and passing the port as an integer argument. The SMTP
object represents a connectedness to an SMTP postal service server and has methods for sending emails. For case, the following phone call creates an SMTP
object for connecting to Gmail:
>>> smtpObj = smtplib.SMTP('smtp.gmail.com', 587) >>> type(smtpObj) <grade 'smtplib.SMTP'>
Entering blazon(smtpObj)
shows you that in that location'due south an SMTP
object stored in smtpObj
. Yous'll need this SMTP
object in order to phone call the methods that log y'all in and send emails. If the smptlib.SMTP()
call is not successful, your SMTP server might not support TLS on port 587. In this instance, you will need to create an SMTP
object using smtplib.SMTP_SSL()
and port 465 instead.
>>> smtpObj = smtplib.SMTP_SSL('smtp.gmail.com', 465)
Annotation
If y'all are not continued to the Internet, Python will raise a socket.gaierror: [Errno 11004] getaddrinfo failed
or like exception.
For your programs, the differences between TLS and SSL aren't important. You merely demand to know which encryption standard your SMTP server uses so you know how to connect to information technology. In all of the interactive beat examples that follow, the smtpObj
variable will contain an SMTP
object returned by the smtplib.SMTP()
or smtplib.SMTP_SSL()
function.
Sending the SMTP "Hi" Message
One time you lot take the SMTP
object, call its oddly named ehlo()
method to "say hello" to the SMTP email server. This greeting is the first step in SMTP and is important for establishing a connection to the server. You don't need to know the specifics of these protocols. Just be sure to call the ehlo()
method commencement thing subsequently getting the SMTP
object or else the subsequently method calls will result in errors. The following is an instance of an ehlo()
telephone call and its return value:
>>> smtpObj.ehlo() (250, b'mx.google.com at your service, [216.172.148.131]\nSIZE 35882577\ n8BITMIME\nSTARTTLS\nENHANCEDSTATUSCODES\nCHUNKING')
If the starting time particular in the returned tuple is the integer 250
(the code for "success" in SMTP), and so the greeting succeeded.
Starting TLS Encryption
If you are connecting to port 587 on the SMTP server (that is, you're using TLS encryption), you'll need to phone call the starttls()
method adjacent. This required step enables encryption for your connection. If you are connecting to port 465 (using SSL), and so encryption is already prepare, and y'all should skip this step.
Here'south an example of the starttls()
method call:
>>> smtpObj.starttls() (220, b'2.0.0 Ready to showtime TLS')
starttls()
puts your SMTP connexion in TLS mode. The 220
in the return value tells you that the server is ready.
Logging in to the SMTP Server
Once your encrypted connectedness to the SMTP server is gear up upward, y'all can log in with your username (commonly your email address) and e-mail password by calling the login()
method.
>>> smtpObj.login(' my_email_address@gmail.com ', ' MY_SECRET_PASSWORD ') (235, b'2.7.0 Accustomed')
Pass a string of your email address every bit the start argument and a string of your countersign as the second argument. The 235
in the render value ways hallmark was successful. Python will raise an smtplib.SMTPAuthenticationError
exception for incorrect passwords.
Alert
Exist careful near putting passwords in your source lawmaking. If anyone e'er copies your plan, they'll have access to your e-mail account! Information technology's a adept thought to call input()
and have the user type in the countersign. Information technology may be inconvenient to take to enter a password each time you run your program, merely this approach will prevent y'all from leaving your countersign in an unencrypted file on your computer where a hacker or laptop thief could easily get it.
Sending an Email
Once you are logged in to your email provider's SMTP server, y'all can call the sendmail()
method to actually send the email. The sendmail()
method call looks like this:
>>> smtpObj.sendmail(' my_email_address@gmail.com ', ' recipient@case.com ', 'Subject area: Then long.\nDear Alice, and then long and thanks for all the fish. Sincerely, Bob') {}
The sendmail()
method requires three arguments.
-
Your electronic mail address every bit a cord (for the email's "from" address)
-
The recipient's e-mail accost as a string or a listing of strings for multiple recipients (for the "to" address)
-
The email torso as a string
The showtime of the email body cord must begin with 'Field of study: \northward'
for the subject line of the electronic mail. The '\n'
newline character separates the subject line from the main body of the email.
The return value from sendmail()
is a dictionary. At that place will exist i key-value pair in the dictionary for each recipient for whom e-mail delivery failed . An empty dictionary ways all recipients were successfully sent the email.
Disconnecting from the SMTP Server
Be sure to telephone call the quit()
method when you are done sending emails. This will disconnect your program from the SMTP server.
>>> smtpObj.quit() (221, b'2.0.0 closing connexion ko10sm23097611pbd.52 - gsmtp')
The 221
in the render value means the session is ending.
To review all the steps for connecting and logging in to the server, sending electronic mail, and disconnection, see Sending Email.
IMAP
Just as SMTP is the protocol for sending email, the Internet Message Admission Protocol (IMAP) specifies how to communicate with an email provider's server to retrieve emails sent to your email address. Python comes with an imaplib
module, merely in fact the third-party imapclient
module is easier to employ. This chapter provides an introduction to using IMAPClient; the full documentation is at http://imapclient.readthedocs.org/ .
The imapclient
module downloads emails from an IMAP server in a rather complicated format. Most probable, y'all'll desire to convert them from this format into simple string values. The pyzmail
module does the hard task of parsing these email messages for yous. You tin discover the complete documentation for PyzMail at http://www.magiksys.net/pyzmail/ .
Install imapclient
and pyzmail
from a Terminal window. Appendix A has steps on how to install third-party modules.
Retrieving and Deleting Emails with IMAP
Finding and retrieving an email in Python is a multistep process that requires both the imapclient
and pyzmail
third-party modules. Only to give yous an overview, hither'south a total example of logging in to an IMAP server, searching for emails, fetching them, and then extracting the text of the e-mail messages from them.
>>> import imapclient >>> imapObj = imapclient.IMAPClient('imap.gmail.com', ssl=True) >>> imapObj.login(' my_email_address@gmail.com ', ' MY_SECRET_PASSWORD ') 'my_email_address@gmail.com Jane Doe authenticated (Success)' >>> imapObj.select_folder('INBOX', readonly=True) >>> UIDs = imapObj.search(['SINCE 05-Jul-2014']) >>> UIDs [40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040, 40041] >>> rawMessages = imapObj.fetch([40041], ['Trunk[]', 'FLAGS']) >>> import pyzmail >>> message = pyzmail.PyzMessage.factory(rawMessages[40041]['BODY[]']) >>> bulletin.get_subject() 'Hello!' >>> message.get_addresses('from') [('Edward Snowden', 'esnowden@nsa.gov')] >>> message.get_addresses('to') [(Jane Doe', 'jdoe@case.com')] >>> message.get_addresses('cc') [] >>> bulletin.get_addresses('bcc') [] >>> message.text_part != None Truthful >>> message.text_part.get_payload().decode(bulletin.text_part.charset) 'Follow the money.\r\northward\r\n-Ed\r\n' >>> bulletin.html_part != None True >>> message.html_part.get_payload().decode(message.html_part.charset) '<div dir="ltr"><div>Then long, and thanks for all the fish!<br><br></div>- Al<br></div>\r\n' >>> imapObj.logout()
You lot don't have to memorize these steps. After we go through each step in detail, yous can come back to this overview to refresh your memory.
Connecting to an IMAP Server
Just like yous needed an SMTP
object to connect to an SMTP server and transport electronic mail, you lot need an IMAPClient
object to connect to an IMAP server and receive email. Beginning yous'll need the domain name of your email provider's IMAP server. This will exist different from the SMTP server's domain name. Table 16-2 lists the IMAP servers for several popular email providers.
Table 16-two. Electronic mail Providers and Their IMAP Servers
Provider | IMAP server domain name |
---|---|
Gmail | imap.gmail.com |
Outlook.com/Hotmail.com | imap-mail.outlook.com |
Yahoo Mail | imap.post.yahoo.com |
AT&T | imap.mail.att.internet |
Comcast | imap.comcast.net |
Verizon | incoming.verizon.cyberspace |
Once you lot accept the domain name of the IMAP server, call the imapclient.IMAPClient()
function to create an IMAPClient
object. About email providers crave SSL encryption, so pass the ssl=True
keyword argument. Enter the following into the interactive trounce (using your provider's domain name):
>>> import imapclient >>> imapObj = imapclient.IMAPClient('imap.gmail.com', ssl=Truthful)
In all of the interactive beat examples in the following sections, the imapObj
variable will incorporate an IMAPClient
object returned from the imapclient.IMAPClient()
function. In this context, a client is the object that connects to the server.
Logging in to the IMAP Server
Once y'all have an IMAPClient
object, telephone call its login()
method, passing in the username (this is usually your e-mail address) and password every bit strings.
>>> imapObj.login(' my_email_address@gmail.com ', ' MY_SECRET_PASSWORD ') 'my_email_address@gmail.com Jane Doe authenticated (Success)'
Warning
Remember, never write a password directly into your code! Instead, design your program to accept the password returned from input()
.
If the IMAP server rejects this username/countersign combination, Python volition raise an imaplib.error
exception. For Gmail accounts, y'all may demand to use an application-specific password; for more details, encounter Gmail'south Awarding-Specific Passwords.
Searching for Email
Once you're logged on, actually retrieving an email that you're interested in is a two-step process. First, you must select a folder yous want to search through. So, you must call the IMAPClient
object'south search()
method, passing in a cord of IMAP search keywords.
Selecting a Folder
Nigh every account has an INBOX
folder by default, but you can also become a list of folders by calling the IMAPClient
object'southward list_folders()
method. This returns a list of tuples. Each tuple contains information about a single folder. Continue the interactive vanquish example by inbound the following:
>>> import pprint >>> pprint.pprint(imapObj.list_folders()) [(('\\HasNoChildren',), '/', 'Drafts'), (('\\HasNoChildren',), '/', 'Filler'), (('\\HasNoChildren',), '/', 'INBOX'), (('\\HasNoChildren',), '/', 'Sent'), --snip- (('\\HasNoChildren', '\\Flagged'), '/', '[Gmail]/Starred'), (('\\HasNoChildren', '\\Trash'), '/', '[Gmail]/Trash')]
This is what your output might wait similar if you have a Gmail account. (Gmail calls its folders labels , just they piece of work the aforementioned way as folders.) The three values in each of the tuples—for example, (('\\HasNoChildren',), '/', 'INBOX')
—are every bit follows:
-
A tuple of the folder's flags. (Exactly what these flags represent is across the scope of this volume, and you tin safely ignore this field.)
-
The delimiter used in the name cord to separate parent folders and subfolders.
-
The full name of the binder.
To select a binder to search through, pass the folder'south proper noun as a string into the IMAPClient
object's select_folder()
method.
>>> imapObj.select_folder('INBOX', readonly=True)
You can ignore select_folder()
's return value. If the selected folder does non exist, Python will raise an imaplib.error
exception.
The readonly=True
keyword argument prevents you from accidentally making changes or deletions to whatsoever of the emails in this folder during the subsequent method calls. Unless you want to delete emails, it'due south a expert thought to always set readonly
to True
.
Performing the Search
With a folder selected, y'all can now search for emails with the IMAPClient
object'due south search()
method. The argument to search()
is a list of strings, each formatted to the IMAP'south search keys. Table 16-iii describes the various search keys.
Table xvi-3. IMAP Search Keys
Search key | Pregnant |
---|---|
| Returns all messages in the folder. You may run in to |
| These three search keys return, respectively, messages that were received by the IMAP server before, on, or after the given |
| Returns messages where |
| Returns all letters where |
| Returns all messages with and without the \Seen flag, respectively. An email obtains the \Seen flag if it has been accessed with a |
| Returns all messages with and without the \Answered flag, respectively. A bulletin obtains the \Answered flag when it is replied to. |
| Returns all messages with and without the \Deleted flag, respectively. Email messages deleted with the |
| Returns all messages with and without the \Draft flag, respectively. Typhoon messages are usually kept in a separate |
| Returns all messages with and without the \Flagged flag, respectively. This flag is commonly used to mark e-mail messages as "Of import" or "Urgent." |
| Returns all letters larger or smaller than |
| Returns the letters that |
| Returns the messages that lucifer either the first or 2d |
Notation that some IMAP servers may have slightly different implementations for how they handle their flags and search keys. It may crave some experimentation in the interactive shell to come across exactly how they behave.
Yous can laissez passer multiple IMAP search key strings in the list argument to the search()
method. The messages returned are the ones that friction match all the search keys. If yous want to lucifer any of the search keys, use the OR
search key. For the Non
and OR
search keys, ane and 2 complete search keys follow the Non
and OR
, respectively.
Hither are some instance search()
method calls along with their meanings:
-
imapObj.search(['ALL'])
. Returns every message in the currently selected folder. -
imapObj.search(['ON 05-Jul-2015'])
. Returns every message sent on July 5, 2022. -
imapObj.search(['SINCE 01-Jan-2015', 'Earlier 01-Feb-2015', 'UNSEEN'])
. Returns every message sent in January 2022 that is unread. (Notation that this means on and after January 1 and upwards to but not including February 1.) -
imapObj.search(['SINCE 01-Jan-2015', 'FROM alice@example.com'])
. Returns every message from alice@example.com sent since the start of 2022. -
imapObj.search(['SINCE 01-Jan-2015', 'NOT FROM alice@example.com'])
. Returns every message sent from everyone except alice@example.com since the kickoff of 2022. -
imapObj.search(['OR FROM alice@example.com FROM bob@example.com'])
. Returns every message ever sent from alice@case.com or bob@example.com . -
imapObj.search(['FROM alice@example.com', 'FROM bob@example.com'])
. Fob case! This search will never render any messages, because messages must friction match all search keywords. Since there tin can be simply one "from" address, information technology is impossible for a message to be from both alice@example.com and bob@example.com .
The search()
method doesn't return the emails themselves only rather unique IDs (UIDs) for the emails, equally integer values. You can then pass these UIDs to the fetch()
method to obtain the email content.
Go on the interactive vanquish example by entering the following:
>>> UIDs = imapObj.search(['SINCE 05-Jul-2015']) >>> UIDs [40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040, 40041]
Here, the list of message IDs (for messages received July five onward) returned by search()
is stored in UIDs
. The listing of UIDs returned on your calculator will be dissimilar from the ones shown here; they are unique to a particular email account. When you later on pass UIDs to other function calls, use the UID values you received, not the ones printed in this book's examples.
Size Limits
If your search matches a large number of email messages, Python might enhance an exception that says imaplib.error: got more 10000 bytes
. When this happens, you lot will have to disconnect and reconnect to the IMAP server and endeavor over again.
This limit is in place to prevent your Python programs from eating up as well much memory. Unfortunately, the default size limit is ofttimes too small. You tin alter this limit from x,000 bytes to 10,000,000 bytes by running this code:
>>> import imaplib >>> imaplib._MAXLINE = 10000000
This should preclude this error bulletin from coming up again. You may want to make these two lines part of every IMAP program yous write.
Fetching an E-mail and Marking Information technology As Read
Once you take a list of UIDs, you can phone call the IMAPClient
object's fetch()
method to get the actual e-mail content.
The list of UIDs will be fetch()
'south first argument. The second argument should be the list ['BODY[]']
, which tells fetch()
to download all the body content for the emails specified in your UID listing.
Let's proceed our interactive shell example.
>>> rawMessages = imapObj.fetch(UIDs, ['BODY[]']) >>> import pprint >>> pprint.pprint(rawMessages) {40040: {'Torso[]': 'Delivered-To: my_email_address@gmail.com\r\northward' 'Received: by 10.76.71.167 with SMTP id ' --snip-- '\r\northward' '------=_Part_6000970_707736290.1404819487066--\r\north', 'SEQ': 5430}}
Import pprint
and pass the render value from fetch()
, stored in the variable rawMessages
, to pprint.pprint()
to "pretty print" it, and you'll meet that this render value is a nested dictionary of letters with UIDs equally the keys. Each bulletin is stored equally a dictionary with two keys: 'Trunk[]'
and 'SEQ'
. The 'Trunk[]'
primal maps to the bodily body of the email. The 'SEQ'
key is for a sequence number , which has a similar office to the UID. You can safely ignore it.
As yous can see, the message content in the 'Body[]'
cardinal is pretty unintelligible. It'south in a format called RFC 822, which is designed for IMAP servers to read. Simply you don't need to sympathise the RFC 822 format; later on in this chapter, the pyzmail
module will brand sense of it for you lot.
When you selected a folder to search through, you called select_folder()
with the readonly=Truthful
keyword argument. Doing this will prevent yous from accidentally deleting an email—but information technology besides ways that emails volition non get marked as read if y'all fetch them with the fetch()
method. If you do want emails to be marked as read when you fetch them, you will need to pass readonly=False
to select_folder()
. If the selected folder is already in readonly manner, you tin reselect the current folder with another call to select_folder()
, this time with the readonly=Imitation
keyword argument:
>>> imapObj.select_folder('INBOX', readonly=False)
Getting E-mail Addresses from a Raw Bulletin
The raw messages returned from the fetch()
method nonetheless aren't very useful to people who merely want to read their electronic mail. The pyzmail
module parses these raw letters and returns them as PyzMessage
objects, which make the subject, body, "To" field, "From" field, and other sections of the e-mail easily accessible to your Python code.
Keep the interactive trounce example with the following (using UIDs from your own email business relationship, not the ones shown here):
>>> import pyzmail >>> bulletin = pyzmail.PyzMessage.mill(rawMessages[40041]['Trunk[]'])
First, import pyzmail
. So, to create a PyzMessage
object of an email, call the pyzmail.PyzMessage.factory()
part and pass it the 'Torso[]'
section of the raw bulletin. Store the effect in bulletin
. At present message
contains a PyzMessage
object, which has several methods that make it easy to get the e-mail'due south subject line, every bit well as all sender and recipient addresses. The get_subject()
method returns the subject as a simple cord value. The get_addresses()
method returns a list of addresses for the field you pass it. For example, the method calls might look like this:
>>> message.get_subject() 'Hello!' >>> message.get_addresses('from') [('Edward Snowden', 'esnowden@nsa.gov')] >>> message.get_addresses('to') [(Jane Doe', 'my_email_address@gmail.com')] >>> message.get_addresses('cc') [] >>> message.get_addresses('bcc') []
Notice that the argument for get_addresses()
is 'from'
, 'to'
, 'cc'
, or 'bcc'
. The render value of get_addresses()
is a list of tuples. Each tuple contains two strings: The outset is the name associated with the e-mail address, and the second is the e-mail accost itself. If at that place are no addresses in the requested field, get_addresses()
returns a bare list. Hither, the 'cc'
carbon copy and 'bcc'
blind carbon copy fields both independent no addresses and so returned empty lists.
Getting the Body from a Raw Bulletin
Emails can be sent as plaintext, HTML, or both. Plaintext emails contain only text, while HTML emails tin can have colors, fonts, images, and other features that brand the email message look like a small web page. If an email is only plaintext, its PyzMessage
object will take its html_part
attributes set to None
. Besides, if an e-mail is but HTML, its PyzMessage
object volition take its text_part
attribute fix to None
.
Otherwise, the text_part
or html_part
value volition have a get_payload()
method that returns the email's body as a value of the bytes data blazon. (The bytes information blazon is across the scope of this volume.) But this still isn't a string value that nosotros tin use. Ugh! The last step is to call the decode()
method on the bytes value returned by get_payload()
. The decode()
method takes one statement: the message's graphic symbol encoding, stored in the text_part.charset
or html_part.charset
aspect. This, finally, will return the cord of the e-mail's trunk.
Continue the interactive shell example by entering the following:
❶ >>> bulletin.text_part != None True >>> message.text_part.get_payload().decode(message.text_part.charset) ❷ 'Then long, and thanks for all the fish!\r\northward\r\n-Al\r\due north' ❸ >>> message.html_part != None True ❹ >>> message.html_part.get_payload().decode(bulletin.html_part.charset) '<div dir="ltr"><div>So long, and thank you for all the fish!<br><br></div>-Al <br></div>\r\due north'
The email we're working with has both plaintext and HTML content, and then the PyzMessage
object stored in message
has text_part
and html_part
attributes not equal to None
❶ ❸. Calling get_payload()
on the bulletin's text_part
and and so calling decode()
on the bytes value returns a string of the text version of the email ❷. Using get_payload()
and decode()
with the bulletin'due south html_part
returns a string of the HTML version of the e-mail ❹.
Deleting Emails
To delete emails, pass a listing of message UIDs to the IMAPClient
object's delete_messages()
method. This marks the emails with the \Deleted flag. Calling the expunge()
method will permanently delete all emails with the \Deleted flag in the currently selected folder. Consider the following interactive shell example:
❶ >>> imapObj.select_folder('INBOX', readonly=False) ❷ >>> UIDs = imapObj.search(['ON 09-Jul-2015']) >>> UIDs [40066] >>> imapObj.delete_messages(UIDs) ❸ {40066: ('\\Seen', '\\Deleted')} >>> imapObj.expunge() ('Success', [(5452, 'EXISTS')])
Hither we select the inbox by calling select_folder()
on the IMAPClient
object and passing 'INBOX'
as the beginning argument; we also pass the keyword argument readonly=False
and then that we can delete emails ❶. Nosotros search the inbox for messages received on a specific engagement and store the returned bulletin IDs in UIDs
❷. Calling delete_message()
and passing it UIDs
returns a dictionary; each key-value pair is a message ID and a tuple of the message'southward flags, which should at present include \Deleted ❸. Calling expunge()
then permanently deletes messages with the \Deleted flag and returns a success message if at that place were no bug expunging the emails. Note that some email providers, such equally Gmail, automatically expunge emails deleted with delete_messages()
instead of waiting for an expunge command from the IMAP client.
Disconnecting from the IMAP Server
When your programme has finished retrieving or deleting emails, just call the IMAPClient's logout()
method to disconnect from the IMAP server.
>>> imapObj.logout()
If your program runs for several minutes or more than, the IMAP server may time out , or automatically disconnect. In this case, the next method telephone call your program makes on the IMAPClient
object will enhance an exception like the following:
imaplib.arrest: socket fault: [WinError 10054] An existing connection was forcibly closed by the remote host
In this upshot, your program will have to call imapclient.IMAPClient()
to connect again.
Whew! That's information technology. In that location were a lot of hoops to jump through, just you at present have a mode to go your Python programs to log in to an email business relationship and fetch emails. You lot can always consult the overview in Retrieving and Deleting Emails with IMAP whenever you demand to call up all of the steps.
Project: Sending Fellow member Ante Reminder Emails
Say you have been "volunteered" to track member ante for the Mandatory Volunteerism Club. This is a truly boring task, involving maintaining a spreadsheet of everyone who has paid each month and emailing reminders to those who haven't. Instead of going through the spreadsheet yourself and copying and pasting the aforementioned email to everyone who is behind on ante, allow's—you guessed it—write a script that does this for you.
At a high level, here's what your program will do:
-
Read information from an Excel spreadsheet.
-
Detect all members who have not paid ante for the latest month.
-
Find their electronic mail addresses and send them personalized reminders.
This means your lawmaking will need to do the following:
-
Open and read the cells of an Excel certificate with the
openpyxl
module. (Meet Chapter 12 for working with Excel files.) -
Create a dictionary of members who are behind on their dues.
-
Log in to an SMTP server past calling
smtplib.SMTP()
,ehlo()
,starttls()
, andlogin()
. -
For all members behind on their ante, send a personalized reminder email past calling the
sendmail()
method.
Open a new file editor window and save information technology as sendDuesReminders.py .
Step 1: Open up the Excel File
Let'southward say the Excel spreadsheet you use to track membership dues payments looks similar Figure 16-2 and is in a file named duesRecords.xlsx . You lot can download this file from http://nostarch.com/automatestuff/ .
Effigy sixteen-2. The spreadsheet for tracking member dues payments
This spreadsheet has every member'due south name and e-mail accost. Each month has a column tracking members' payment statuses. The jail cell for each fellow member is marked with the text paid once they have paid their ante.
The program will accept to open up duesRecords.xlsx and effigy out the column for the latest month by calling the get_highest_column()
method. (You tin consult Chapter 12 for more than data on accessing cells in Excel spreadsheet files with the openpyxl
module.) Enter the post-obit lawmaking into the file editor window:
#! python3 # sendDuesReminders.py - Sends emails based on payment condition in spreadsheet. import openpyxl, smtplib, sys # Open up the spreadsheet and get the latest dues status. ❶ wb = openpyxl.load_workbook('duesRecords.xlsx') ❷ canvas = wb.get_sheet_by_name('Sheet1') ❸ lastCol = canvas.get_highest_column() ❹ latestMonth = sheet.cell(row=1, column=lastCol).value # TODO: Check each member's payment status. # TODO: Log in to email account. # TODO: Ship out reminder emails.
After importing the openpyxl
, smtplib
, and sys
modules, nosotros open our duesRecords.xlsx file and shop the resulting Workbook
object in wb
❶. Then we become Sheet ane and store the resulting Worksheet
object in sheet
❷. Now that we have a Worksheet
object, we can access rows, columns, and cells. We store the highest column in lastCol
❸, and we then use row number ane and lastCol
to admission the cell that should concord the about recent month. We get the value in this jail cell and store it in latestMonth
❹.
Step 2: Find All Unpaid Members
Once yous've determined the column number of the latest month (stored in lastCol
), you can loop through all rows after the get-go row (which has the cavalcade headers) to see which members have the text paid in the cell for that month's dues. If the member hasn't paid, you lot can grab the member's proper name and email accost from columns i and 2, respectively. This data will go into the unpaidMembers
dictionary, which will track all members who oasis't paid in the virtually recent month. Add the following lawmaking to sendDuesReminder.py .
#! python3 # sendDuesReminders.py - Sends emails based on payment status in spreadsheet. --snip-- # Bank check each member'south payment condition. unpaidMembers = {} ❶ for r in range(2, sheet.get_highest_row() + 1): ❷ payment = sheet.cell(row=r, cavalcade=lastCol).value if payment != 'paid': ❸ name = sheet.cell(row=r, column=1).value ❹ email = sheet.cell(row=r, column=2).value ❺ unpaidMembers[proper name] = e-mail
This code sets upwards an empty dictionary unpaidMembers
and so loops through all the rows subsequently the start ❶. For each row, the value in the most recent column is stored in payment
❷. If payment
is not equal to 'paid'
, so the value of the first cavalcade is stored in name
❸, the value of the second cavalcade is stored in email
❹, and name
and electronic mail
are added to unpaidMembers
❺.
Step 3: Send Customized Email Reminders
In one case y'all accept a list of all unpaid members, it'due south time to send them email reminders. Add the following code to your program, except with your real email accost and provider data:
#! python3 # sendDuesReminders.py - Sends emails based on payment status in spreadsheet. --snip-- # Log in to email business relationship. smtpObj = smtplib.SMTP('smtp.gmail.com', 587) smtpObj.ehlo() smtpObj.starttls() smtpObj.login(' my_email_address@gmail.com ', sys.argv[1])
Create an SMTP
object past calling smtplib.SMTP()
and passing it the domain proper noun and port for your provider. Call ehlo()
and starttls()
, and and so call login()
and pass it your email address and sys.argv[1]
, which will store your password string. You'll enter the password as a command line argument each time you run the programme, to avoid saving your password in your source code.
Once your programme has logged in to your electronic mail account, information technology should get through the unpaidMembers
dictionary and send a personalized e-mail to each fellow member's e-mail address. Add the following to sendDuesReminders.py :
#! python3 # sendDuesReminders.py - Sends emails based on payment condition in spreadsheet. --snip-- # Ship out reminder emails. for name, e-mail in unpaidMembers.items(): ❶ body = "Subject field: %s dues unpaid.\nDear %southward,\nRecords show that you accept not paid ante for %south. Please brand this payment every bit shortly as possible. Thank y'all!'" % (latestMonth, name, latestMonth) ❷ print('Sending email to %south...' % email) ❸ sendmailStatus = smtpObj.sendmail(' my_email_address@gmail.com ', email, body) ❹ if sendmailStatus != {}: print('There was a problem sending email to %s: %s' % (email, sendmailStatus)) smtpObj.quit()
This code loops through the names and emails in unpaidMembers
. For each member who hasn't paid, nosotros customize a message with the latest calendar month and the member's proper noun, and store the message in body
❶. Nosotros print output maxim that nosotros're sending an email to this fellow member's email accost ❷. Then we call sendmail()
, passing it the from address and the customized bulletin ❸. We store the render value in sendmailStatus
.
Call up that the sendmail()
method will render a nonempty dictionary value if the SMTP server reported an error sending that particular e-mail. The last function of the for
loop at ❹ checks if the returned lexicon is nonempty, and if it is, prints the recipient's electronic mail accost and the returned lexicon.
Later on the programme is done sending all the emails, the quit()
method is called to disconnect from the SMTP server.
When you run the program, the output will look something like this:
Sending email to alice@instance.com... Sending email to bob@example.com... Sending email to eve@case.com...
The recipients will receive an email that looks like Figure sixteen-3.
Figure xvi-iii. An automatically sent email from sendDuesReminders.py
Sending Text Messages with Twilio
Nearly people are more likely to be virtually their phones than their computers, then text messages can be a more immediate and reliable mode of sending notifications than email. Also, the brusk length of text messages makes it more than likely that a person will get around to reading them.
In this section, you'll larn how to sign up for the gratis Twilio service and use its Python module to transport text letters. Twilio is an SMS gateway service , which means it's a service that allows yous to send text messages from your programs. Although you will exist express in how many texts you can send per month and the texts will exist prefixed with the words Sent from a Twilio trial account , this trial service is probably adequate for your personal programs. The complimentary trial is indefinite; you lot won't have to upgrade to a paid plan later.
Twilio isn't the only SMS gateway service. If you prefer not to use Twilio, you can find culling services by searching online for free sms gateway , python sms api , or even twilio alternatives .
Before signing up for a Twilio account, install the twilio
module. Appendix A has more details about installing tertiary-party modules.
Note
This section is specific to the United States. Twilio does offer SMS texting services for countries outside of the United States, but those specifics aren't covered in this book. The twilio
module and its functions, still, will work the aforementioned outside the United States. Come across http://twilio.com/ for more information.
Signing Upward for a Twilio Account
Go to http://twilio.com/ and fill out the sign-up form. Once you've signed up for a new account, you lot'll need to verify a mobile phone number that you want to transport texts to. (This verification is necessary to prevent people from using the service to spam random phone numbers with text messages.)
After receiving the text with the verification number, enter it into the Twilio website to prove that you own the mobile telephone you are verifying. You lot will now be able to transport texts to this phone number using the twilio
module.
Twilio provides your trial account with a phone number to utilise as the sender of text letters. You will need two more pieces of data: your account SID and the auth (hallmark) token. You tin can observe this information on the Dashboard page when you are logged in to your Twilio account. These values human action as your Twilio username and password when logging in from a Python program.
Sending Text Messages
Once yous've installed the twilio
module, signed up for a Twilio account, verified your phone number, registered a Twilio telephone number, and obtained your business relationship SID and auth token, you will finally exist ready to transport yourself text messages from your Python scripts.
Compared to all the registration steps, the bodily Python lawmaking is fairly elementary. With your computer connected to the Internet, enter the following into the interactive beat out, replacing the accountSID
, authToken
, myTwilioNumber
, and myCellPhone
variable values with your real information:
❶ >>> from twilio.rest import TwilioRestClient >>> accountSID = ' ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ' >>> authToken = ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ' ❷ >>> twilioCli = TwilioRestClient(accountSID, authToken) >>> myTwilioNumber = '+14955551234' >>> myCellPhone = '+14955558888' ❸ >>> message = twilioCli.messages.create(torso='Mr. Watson - Come here - I desire to run across you.', from_=myTwilioNumber, to=myCellPhone)
A few moments after typing the terminal line, you lot should receive a text message that reads Sent from your Twilio trial account - Mr. Watson - Come here - I want to meet you .
Because of the way the twilio
module is set up, you need to import it using from twilio.rest import TwilioRestClient
, non but import twilio
❶. Shop your account SID in accountSID
and your auth token in authToken
and and then telephone call TwilioRestClient()
and laissez passer it accountSID
and authToken
. The call to TwilioRestClient()
returns a TwilioRestClient
object ❷. This object has a messages
attribute, which in plow has a create()
method you can use to send text messages. This is the method that will instruct Twilio's servers to transport your text bulletin. Later on storing your Twilio number and cell phone number in myTwilioNumber
and myCellPhone
, respectively, call create()
and pass it keyword arguments specifying the trunk of the text message, the sender's number (myTwilioNumber
), and the recipient'southward number (myCellPhone
) ❸.
The Message
object returned from the create()
method will accept data near the text bulletin that was sent. Proceed the interactive shell instance past entering the following:
>>> message.to '+14955558888' >>> message.from _ '+14955551234' >>> message.torso 'Mr. Watson - Come here - I want to see you.'
The to
, from_
, and body
attributes should concord your cell phone number, Twilio number, and bulletin, respectively. Note that the sending phone number is in the from_
attribute—with an underscore at the end—non from
. This is because from
is a keyword in Python (yous've seen it used in the from
modulename import *
course of import
statement, for example), then information technology cannot be used as an attribute proper name. Continue the interactive shell example with the following:
>>> bulletin.status 'queued' >>> message.date_created datetime.datetime(2015, 7, eight, ane, 36, 18) >>> message.date_sent == None True
The status
attribute should give you a cord. The date_created
and date_sent
attributes should give you a datetime
object if the message has been created and sent. It may seem odd that the status
attribute is ready to 'queued'
and the date_sent
aspect is set up to None
when you've already received the text message. This is because you captured the Message
object in the message
variable earlier the text was actually sent. You lot will need to refetch the Message
object in lodge to run across its most up-to-date condition
and date_sent
. Every Twilio message has a unique string ID (SID) that tin be used to fetch the latest update of the Message
object. Continue the interactive shell case by entering the following:
>>> message.sid 'SM09520de7639ba3af137c6fcb7c5f4b51' ❶ >>> updatedMessage = twilioCli.messages.get(message.sid) >>> updatedMessage.status 'delivered' >>> updatedMessage.date_sent datetime.datetime(2015, 7, viii, 1, 36, 18)
Entering bulletin.sid
bear witness you this message's long SID. Past passing this SID to the Twilio client's get()
method ❶, you can think a new Bulletin
object with the most up-to-date information. In this new Message
object, the status
and date_sent
attributes are correct.
The condition
attribute volition be ready to 1 of the post-obit string values: 'queued'
, 'sending'
, 'sent'
, 'delivered'
, 'undelivered'
, or 'failed'
. These statuses are self-explanatory, but for more precise details, take a look at the resources at http://nostarch.com/automatestuff/ .
Project: "Simply Text Me" Module
The person you'll virtually oft text from your programs is probably you. Texting is a dandy way to send yourself notifications when you're abroad from your figurer. If you've automated a boring job with a programme that takes a couple of hours to run, you could accept it notify you with a text when it'due south finished. Or you may accept a regularly scheduled programme running that sometimes needs to contact you lot, such as a atmospheric condition-checking plan that texts you a reminder to pack an umbrella.
As a uncomplicated example, hither's a small Python plan with a textmyself()
function that sends a message passed to it as a cord statement. Open a new file editor window and enter the following lawmaking, replacing the account SID, auth token, and telephone numbers with your own information. Relieve it every bit textMyself.py .
#! python3 # textMyself.py - Defines the textmyself() function that texts a message # passed to information technology as a string. # Preset values: accountSID = ' ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ' authToken = ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ' myNumber = '+15559998888' twilioNumber = '+15552225678' from twilio.rest import TwilioRestClient ❶ def textmyself(message): ❷ twilioCli = TwilioRestClient(accountSID, authToken) ❸ twilioCli.letters.create(body=bulletin, from_=twilioNumber, to=myNumber)
This plan stores an account SID, auth token, sending number, and receiving number. It then defined textmyself()
to take on statement ❶, brand a TwilioRestClient
object ❷, and call create()
with the bulletin you passed ❸.
If you want to brand the textmyself()
function available to your other programs, simply place the textMyself.py file in the aforementioned folder as the Python executable ( C:\Python34 on Windows, /usr/local/lib/python3.iv on OS Ten, and /usr/bin/python3 on Linux). Now you can apply the function in your other programs. Whenever you desire i of your programs to text you, just add the following:
import textmyself textmyself.textmyself('The boring task is finished.')
You need to sign upward for Twilio and write the texting code just once. After that, information technology'southward but ii lines of lawmaking to send a text from whatsoever of your other programs.
Summary
We communicate with each other on the Internet and over cell phone networks in dozens of different means, but email and texting predominate. Your programs can communicate through these channels, which gives them powerful new notification features. You can even write programs running on different computers that communicate with one another direct via email, with one program sending emails with SMTP and the other retrieving them with IMAP.
Python's smtplib
provides functions for using the SMTP to transport emails through your e-mail provider's SMTP server. Too, the third-political party imapclient
and pyzmail
modules permit you access IMAP servers and think emails sent to you. Although IMAP is a bit more involved than SMTP, it's also quite powerful and allows you to search for particular emails, download them, and parse them to extract the subject and body as string values.
Texting is a bit unlike from email, since, unlike email, more than just an Internet connection is needed to send SMS texts. Fortunately, services such every bit Twilio provide modules to allow you to send text messages from your programs. In one case you go through an initial setup procedure, y'all'll exist able to send texts with simply a couple lines of lawmaking.
With these modules in your skill ready, you'll be able to program the specific atmospheric condition under which your programs should send notifications or reminders. Now your programs will take reach far beyond the figurer they're running on!
Practice Questions
Q: | 1. What is the protocol for sending electronic mail? For checking and receiving electronic mail? |
Q: | two. What four |
Q: | 3. What ii |
Q: | 4. What kind of argument do you lot pass to |
Q: | 5. What do you exercise if your code gets an error message that says |
Q: | 6. The |
Q: | seven. What three pieces of information practise you need from Twilio before y'all tin send text messages? |
Exercise Projects
For practice, write programs that do the post-obit.
Random Chore Assignment Emailer
Write a program that takes a list of people's email addresses and a list of chores that need to exist done and randomly assigns chores to people. Email each person their assigned chores. If you're feeling ambitious, keep a tape of each person's previously assigned chores and then that you can make sure the programme avoids assigning anyone the same chore they did last time. For another possible feature, schedule the plan to run once a calendar week automatically.
Here's a hint: If you pass a list to the random.choice()
role, it will return a randomly selected particular from the list. Role of your code could look like this:
chores = ['dishes', 'bath', 'vacuum', 'walk domestic dog'] randomChore = random.choice(chores) chores.remove(randomChore) # this chore is at present taken, then remove it
Umbrella Reminder
Chapter 11 showed you how to use the requests
module to scrape data from http://atmospheric condition.gov/ . Write a program that runs merely before y'all wake up in the morning and checks whether it's raining that day. If so, have the programme text y'all a reminder to pack an umbrella before leaving the firm.
Auto Unsubscriber
Write a program that scans through your electronic mail account, finds all the unsubscribe links in all your emails, and automatically opens them in a browser. This program will accept to log in to your email provider's IMAP server and download all of your emails. You can utilise BeautifulSoup (covered in Affiliate 11) to bank check for whatsoever example where the give-and-take unsubscribe occurs inside an HTML link tag.
Once you have a list of these URLs, you can use webbrowser.open up()
to automatically open all of these links in a browser.
You'll still take to manually get through and complete any additional steps to unsubscribe yourself from these lists. In most cases, this involves clicking a link to ostend.
Simply this script saves you from having to go through all of your emails looking for unsubscribe links. You can and so pass this script forth to your friends so they can run it on their email accounts. (Just make sure your e-mail password isn't hardcoded in the source code!)
Controlling Your Computer Through Electronic mail
Write a program that checks an e-mail account every xv minutes for any instructions you lot email it and executes those instructions automatically. For example, BitTorrent is a peer-to-peer downloading system. Using free BitTorrent software such as qBittorrent, you can download large media files on your home computer. If you email the program a (completely legal, not at all piratical) BitTorrent link, the program will eventually check its email, find this message, excerpt the link, and then launch qBittorrent to start downloading the file. This fashion, you can have your home computer begin downloads while you're away, and the (completely legal, not at all piratical) download can be finished by the time y'all render home.
Affiliate fifteen covers how to launch programs on your computer using the subprocess.Popen()
office. For case, the following call would launch the qBittorrent program, along with a torrent file:
qbProcess = subprocess.Popen(['C:\\Program Files (x86)\\qBittorrent\\ qbittorrent.exe', 'shakespeare_complete_works.torrent'])
Of grade, you'll want the program to brand sure the emails come from yous. In item, you might want to require that the emails contain a countersign, since it is fairly trivial for hackers to false a "from" address in emails. The program should delete the emails information technology finds and then that it doesn't repeat instructions every time it checks the electronic mail account. Every bit an extra characteristic, have the programme email or text you a confirmation every time information technology executes a control. Since yous won't be sitting in front of the calculator that is running the program, it's a expert idea to utilise the logging functions (come across Chapter 10) to write a text file log that you lot tin check if errors come upward.
qBittorrent (every bit well equally other BitTorrent applications) has a feature where it can quit automatically subsequently the download completes. Chapter fifteen explains how yous can determine when a launched application has quit with the wait()
method for Popen
objects. The wait()
method call volition cake until qBittorrent has stopped, and so your program can email or text you a notification that the download has completed.
There are a lot of possible features you could add to this project. If y'all get stuck, you can download an case implementation of this plan from http://nostarch.com/automatestuff/ .
How To Set Up A Boring Bar,
Source: https://automatetheboringstuff.com/chapter16/
Posted by: summerseldis1991.blogspot.com
0 Response to "How To Set Up A Boring Bar"
Post a Comment