[go: up one dir, main page]

Showing posts with label Gmail. Show all posts
Showing posts with label Gmail. Show all posts

Tuesday, December 6, 2016

Modifying email signatures with the Gmail API

NOTE: The content here is also available as a video and overview post, part of this series.

UPDATE (Feb 2017): Tweaked the code sample as the isPrimary flag may be missing from non-primary aliases; also added link above to video.

Introduction

In a previous post, I introduced Python developers to the Gmail API with a tutorial on how to search for threads with a minimum number of messages. Today, we'll explore another part of the API, covering the settings endpoints that were added in mid-2016. What's the big deal? Well, you couldn't use the API to read nor modify user settings before, and now you can!

One example all of us can relate to is your personal email signature. Wouldn't it be great if we could modify it programmatically, say to include some recent news about you (perhaps a Tweet other social post), or maybe some random witty quote? You could then automate it to change once a quarter, or even hourly if you like being truly random!

Using the Gmail API

Our simple Python script won't be sending email nor reading user messages, so the only authorization scope needed is the one that accesses basic user settings (there's another for more sensitive user settings):
  • https://www.googleapis.com/auth/gmail.settings.basic — Manage basic Gmail user settings
See the documentation for a list of all Gmail API scopes and what each of them mean. Since we've fully covered the authorization boilerplate in earlier posts and videos, including how to connect to the Gmail API, we're going to skip that here and jump right to the action. You can copy the boilerplate from other scripts you've written. Regardless, be sure to create an service endpoint to the API:

GMAIL = discovery.build('gmail', 'v1',
    http=creds.authorize(Http()))


What are "sendAs" email addresses?

First, a quick word about "sendAs" email addresses. Gmail lets you send email from addresses other than your actual Gmail address (considered your primary address). This lets you manage multiple accounts from the same Gmail user interface. (As expected, you need to own or otherwise have access to the alternate email addresses in order to do this.) However, most people only use their primary address, so you may not know about it. You can learn more about sendAs addresses here and here.

Now you may be tempted to use the term "alias," especially because that word was mentioned in those Help pages you just looked at right? However for now, I'd recommend trying to avoid that terminology as it refers to something else in a G Suite/Google Apps context. Can't you see how we already got distracted from the core reason for this post? See, you almost forgot about email signatures already, right? If you stick with "sender addresses" or "sendAs email addresses," there won’t be any confusion.

Using a "Quote of the Day" in your email signature

The Python script we're exploring in this post sets a "Quote of the Day" (or "QotD" for short) as the signature of your primary sendAs address. Where does the QotD come from? Well, it can be as simple (and boring) as this function that returns a hardcoded string:



Cute but not very random right? A better idea is to choose from a number of quotes you have in a relational database w/columns for quotes & authors. Here’s some sample code for data in a SQLite database:



More random, which is cool, but this particular snippet isn't efficient because we’re selecting all rows and then choosing a quote randomly. Obviously there's a better way if a database is your data source. I prefer using a web service instead, coming in the form of a REST API. The code snippet here does just that:



You only need to find a quote-of-the-day service and provide its URL on line 8 that returns a JSON payload. Obviously you'll need a bit more scaffolding if this were a real service, but in this pseudocode example, you can assume that using urllib.{,request.}urlopen() works where the service sends back an empty string upon failure. To play it safe, this snippet falls back to the hardcoded string we saw earlier if the service doesn't return a quote, which comes back as a 2-tuple representing quote and author, respectively.

Setting your new email signature

Now that we're clear on the source for the QotD, we can focus on actually setting it as your new email signature. To do that, we need to get all of your sender (sendAs email) addresses—the goal is only to change your primary addresses (and none of the others if you have any):
addresses = GMAIL.users().settings().sendAs().list(userId='me',
    fields='sendAs(isPrimary,sendAsEmail)').execute().get('sendAs')
As in our other Gmail example, a userId of 'me' indicates the currently-authenticated user. The API will return a number of attributes. If know exactly which ones we want, we can specify them in with the fields attribute so as to control size of the return payload which may contribute to overall latency. In our case, we're requesting just the sendAs.isPrimary flag and sendAs.sendAsEmail, the actual email address string of the sender addresses. What's returned is a Python list consisting of all of your sendAs email addresses, which we cycle through to find the primary address:
for address in addresses:
    if address.get('isPrimary'):
        break
One of your sender addresses must be primary, so unless there's a bug in Gmail, when control of the for loop concludes, address will point to your primary sender address. Now all you have to do is set the signature and confirm to the user:
rsp = GMAIL.users().settings().sendAs().patch(userId='me',
        sendAsEmail=address['sendAsEmail'], body=DATA).execute()
print("Signature changed to '%s'" % rsp['signature'])
If you only have one sender address, there's no need request all the addresses and loop through them looking for the primary address as we did above. In such circumstances, that entire request and loop are extraneous... just pass your email address as the sendAsEmail argument, like this:
rsp = GMAIL.users().settings().sendAs().patch(userId='me',
        sendAsEmail=YOUR_EMAIL_ADDR_HERE, body=DATA).execute()

Conclusion

That's all there is... just 26 lines of code. If we use the static string qotd() function above, your output when running this script will look like this:
$ python gmail_change_sig.py # or python3
Signature changed to '"I heart cats."  ~anonymous'
$
Below is the entire script for your convenience which runs on both Python 2 and Python 3 (unmodified!). By using, copying, and/or modifying this code or any other piece of source from this blog, you implicitly agree to its Apache2 license:
from __future__ import print_function

from apiclient import discovery
from httplib2 import Http
from oauth2client import file, client, tools

import qotd
DATA = {'signature': qotd.qotd()}   # quote source up-to-you!

SCOPES = 'https://www.googleapis.com/auth/gmail.settings.basic'
store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
    flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
    creds = tools.run_flow(flow, store)
GMAIL = discovery.build('gmail', 'v1', http=creds.authorize(Http()))
# this entire block optional if you only have one sender address
addresses = GMAIL.users().settings().sendAs().list(userId='me',
        fields='sendAs(isPrimary,sendAsEmail)').execute().get('sendAs')
for address in addresses:
    if address.get('isPrimary'):
        break
rsp = GMAIL.users().settings().sendAs().patch(userId='me',
        sendAsEmail=address['sendAsEmail'], body=DATA).execute()
print("Signature changed to '%s'" % rsp['signature'])
As with our other code samples, you can now customize for your own needs, for a mobile frontend, sysadmin script, or a server-side backend, perhaps accessing other Google APIs.

Code challenge

Want to exercise your newfound knowledge of using the Gmail API's settings endpoints? Write a script that uses the API to manage filters or configure a vacation responder. HINT: take a look at the official Gmail API docs, including the pages specific to filters and vacation settings.

Friday, April 6, 2012

Integrating Google APIs and Technologies

In 1997, long before my tenure at Google, I became a member of the Python community in helping to create Yahoo!Mail, one of the most popular web-based email systems in the world. There were only two Python books on the market back then, and neither addressed my developer’s need to learn Python quickly and competently, so I had to resort to the online docs. This absence, and consequently my development of class materials for a Python course, inspired me to write Prentice Hall’s bestselling Core Python Programming over a decade ago. Since then, I’ve used Python to work on all kinds of interesting applications, from web-based e-mail to geolocalized product search engines, social media games, antispam/antivirus e-mail appliances, and most interestingly, software for doctors to help them analyze and assess patients with spinal fractures. (Ask me about osteoporosis!)

Today at Google, my work involves advocating our tools and APIs to the global developer community. Now that I've been part of the Google family for the past 2.5 years, I thought it would be fun to integrate some of our technologies into the book. With the just-published 3rd edition, readers will find revised but also brand new material they can use to build real applications with. Some of the Google technologies I've integrated into Core Python Applications Programming include accessing your Gmail, parsing Google News XML feeds, and a complete chapter on cloud computing with Google App Engine. It’s also the first published book to feature code that utilizes the Google+ API. While the book contains a longer example using that API, I want to show you how easy it is to connect to Google+ using Python right now!

The bulk of the work in connecting to Google+ (and other Google APIs) is done by my fellow colleagues who maintain the Google APIs Client Library for Python, easily downloaded with pip or easy_install as "google-api-python-client." With this library, the most difficult step to connect with your API of choice has basically been reduced to a single line... see the fourth line of this short Python 2.x example:

# plus.py (by Wesley Chun under CC-SA3.0 license)
from apiclient import discovery

API_KEY = YOUR_KEY_FROM_CONSOLE_API_ACCESS_PAGE
service = discovery.build("plus", "v1", developerKey=API_KEY)
feed = service.activities().search(query='android').execute()
for record in feed['items']:
post = ' '.join(record['title'].strip().split())
if post:
print '\nFrom:', record['actor']['displayName']
print 'Post:', post
print 'Date:', record['published']

In that one line of code (italicized above), we use the Google APIs Client Library's apiclient.discovery.build() method, passing in: a) the desired API ("plus" for Google+), b) the version (currently "v1"), and c) the API key you obtained from your project's development console in the "Simple API Access" section. This key gives your project access to APIs that do not need to access user data. Once we have a handle to the service, we can execute generic queries on the available data stream.

In this code snippet, we're simply querying for the latest (public) Google+ posts that are related to Android and displaying them on the command-line (code can be easily repurposed into any mobile or web application). Naturally, you need to go through the OAuth flow if you do want access to authenticated data. Give it a try!

If you like the code, dig into Core Python Applications Programming for a longer, more detailed example. Both scripts can be downloaded at the website (the code is part of Chapter 15), and you can get involved in the conversation on the Google+ page. I'm open to all feedback, suggestions, and fixes. You can find me at +wescpy or @wescpy. Looking forward to meeting you at an upcoming Google or Python event or in one of my public courses!