Google Cloud Platform

DeepBreath: Preventing angry emails with machine learning

This Chrome plugin could inspire you to use Cloud Natural Language API for your own practical projects. 

We all have bad days. Maybe deadlines are slipping, your cat destroyed your couch (again), or you just have a regular case of the Mondays. Whatever the source of your stress, you hit "Send" on a Gmail draft at work, and you immediately regret it. No matter what, you never want to send excessively emotional or angry emails to coworkers, clients or even friends.

Inspired by many other fun use cases of Google Cloud Natural Language API, we wrote a Chrome plugin called DeepBreath that automatically sends all your saved drafts to Cloud Natural Language API for sentiment analysis. The API automatically detects how positive or negative any given piece of text is with a simple API call, so a plugin to solve the angry email problem was very easy and quick to build for Gmail, and could also be easily repurposed for any other places you write text (forums, project management tools, etc). Please see "A Note On User Data Privacy" below before considering making these extensions.

If your email is of sufficiently negative magnitude, it will automatically display a warning so you can consider a rewrite before you hit send, rather than after. The warning gives you a chance to take a literal deep breath and reconsider the contents of the email.

How does it work? Every time a draft is saved, the body of the draft is sent to the analyzeSentiment API endpoint. A score (the positive or negative sentiment) and the magnitude (how strong the feeling is) is returned. You can read more about score and magnitude in the docs. If the score is sufficiently negative and the magnitude sufficiently strong, a warning pops up. Only one warning pops up per draft.


Running the code

It's extremely easy to run the code. Simply follow the steps below:
  1. Create a Google Cloud Project (if you're new, sign up for a 12-month trial at no cost) and open the Google Cloud Platform Console.
  2. Using the menu in the top left, click API Manager->Credentials->Create Credentials->API Key.
  3. Clone the project from the Github repo.
  4. Copy your API key into the API_KEY variable in main.js
  5. From Chrome, go to the Menu, Extensions->Load Unpacked Extensions, then select the folder with the repo
That's it! Now, if your emails get too negative, a warning will automatically pop up.

Understanding the code

Interacting with Gmail

To start building the DeepBreath extension, we first needed an easy way to interact with Gmail from Javascript. Fortunately, we were able to use gmail.js by KartikTalwar, which he graciously provides under a permissive MIT license. gmail.js provides a hook so that your function can run any time a new draft is saved:

  gmail = new Gmail();
    gmail.observe.on('save_draft', function(id, url, body, xhr)

Understanding API request and response objects

Next, we'll need to understand Cloud Natural Language API. From the docs, we can use the left-side menu to explore the REST or gRPC API endpoints. Make sure you're using the v1 endpoint. Since we're calling the API from Chrome, we can only use REST. We can take a look at the analyzeSentiment API reference docs to understand the structure of the request and response. Since most email drafts are plain text encoded in UTF-8, we simply send the entire draft of the body with those parameters, and most of the other parameters, such as language, are left to their defaults.

  var request = {
    document: {
       type: "PLAIN_TEXT",
       content: body.body
    encodingType: "UTF8"  

Authentication and client libraries

Next, we had to make sure we're actually authenticated to access the API on the project's behalf and call the API.

Authentication can be a tricky issue overall, and we'd recommend reading our Google Cloud authentication guide. Most of the time when interacting with Google APIs, you would use some sort of OAuth flow, using a client ID by either signing in as yourself in a browser (such as our Google Cloud CLI command `gcloud auth application-default login`) or using a Service Account.

OAuth client IDs have a lot of advantages since they directly identify the specific client that's calling your code, and can have more fine-grained access control policies through a combination of scopes and IAM permissions. However, they require an OAuth flow to obtain an access token.

In this repo, we went with something much simpler: API keys. Not all APIs support API keys, but the Natural Language API does. Since the API key is sent along with the plugin, anyone you share the plugin with could use your API key to use up all your quota, so be careful! The big advantage of an API key compared to the OAuth authentication flows is its simplicity.

You can create an API key from the Cloud Platform Console under API Manager->Credentials->Create Credentials->API Key.

We considered the Google API client library for Javascript, but it doesn't work in Chrome extensions. But since we're making a single API call with an API key, it's really easy to just make a REST call using plain vanilla Javascript. We make the call to the URL provided in the reference docs, and add the API key as a URL parameter:" + API_KEY,

Using this URL, which includes the API key along with it for authentication, we can now use vanilla XMLHTTPRequest to make an AJAX call from Chrome."POST", "" + API_KEY, true);
nlpreq.setRequestHeader("Content-type", "application/json");
var request = {
 document: {
   type: "PLAIN_TEXT",
   content: body.body
 encodingType: "UTF8"

Deciding on appropriate sentiment score thresholds using API explorer

Next, we look at the expected response. The response contains the sentiment for the entire document as well as individual sentences. We're only concerned about the sentiment for the entire draft, so we only consider "documentSentiment" in the response. As mentioned earlier, the sentiment tells us how positive or negative the text is, and the magnitude tells us how strong that positive or negative sentiment is. Since we're concerned about drafts that are strongly negative, we want to make sure the sentiment is sufficiently negative and the magnitude is sufficiently high. We played with different parameters on different drafts, and arrived at a sentiment threshold of -0.4 and a magnitude of 0.5. Arriving at these numbers is mostly a guess-and-check game that you might tweak.

  if (sentiment < -0.4 && magnitude > 0.5) {

If we wanted to play around with different thresholds in the browser, we could use the Google API explorer. Here are some sample queries that helped us arrive at the threshold numbers.

TextSentimentMagnitudeToo negative?
I'm really unsure about this product direction.-0.20.2No
These OKRs are entirely unrealistic to finish this quarter.-0.80.8Yes
It looks ok, but there's still room for improvement.-0.30.3No

A note on user data privacy

DeepBreath itself stores no user data, but it does take data from Gmail and sends it to the Natural Language API. See Google's Privacy Policy for how Google handles sensitive user data. If you were to extend this plugin to take other personal data and then seek to publish your extended plugin, it's important that your Chrome extension's description clearly explains how private user data is stored and handled by your extension, as well as link to Google's Privacy Policy as it relates to the use of the Natural Language API. See the Chrome's Webstore's User Data Privacy Policies for more information.

Relax - we're all done!

Hopefully, DeepBreath provides some inspiration for how you can use Cloud Natural Language API for your own practical projects. You're also welcome to run the plugin yourself and submit issues and pull requests to the repos, or fork it yourself if you have ideas on how to change or improve it. For example, besides Gmail, you could do something similar for internal company message boards. See "A Note on User Data Privacy" above for guidelines on user privacy policies.

To learn more about Cloud Natural Language API, see also: