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
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'): breakOne 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 stringqotd()
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.
can you update the script to python 8 as well as google auth?
ReplyDeletePython 8 won't be out for a long time (as the core team is currently thinking about Python 4 at this time). As far as using `google.auth` goes: 1) `oauth2client` is deprecated but not being shut down yet, so you can continue to use this code as-is for now. 2) I'm working on a blog post that differentiates the changes needed to use `google.auth`, including the differences b/w both libraries, and a before/after or compare/contrast a twin sample demo so you can see the required changes side-by-side. I hope to have it published before the end of the year on an official Google (not this) blog.
Delete