DEV Community

Adam.S
Adam.S

Posted on • Updated on • Originally published at bas-man.dev

Managing your Labels in Gmail

Part 2 in a series of articles on implementing a notification system using Gmail and Line Bot

Hi again. If you followed the guide I referenced in the previous post. A Beginner’s Guide to the Gmail API and Its Documentation. The next section will be somewhat familiar.

In this section we will be accessing the labels used within Gmail, creating a new label, and also getting the new label's id. The id is critical because the google API does not use the label's name. It is merely the name displayed in the user interface.

I am going to structure some of the code also at this point.

This will be the new starting code.

import pickle, os.path, sys
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.modify',
          'https://www.googleapis.com/auth/gmail.labels']

def get_service():
    """Shows basic usage of the Gmail API.
    Lists the user's Gmail labels.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    # Another option to ignore google cache logging issue
    # service = build('gmail', 'v1', credentials=creds, cache_discovery=False)
    service = build('gmail', 'v1', credentials=creds)
    return service


def main():
    service = get_service()

if __name__ == '__main__':
    main()


Enter fullscreen mode Exit fullscreen mode

I have moved the code that will connect to the API into its own function call get_service which returns an object we can use to access the API.
The keen reader my have noticed that I have updated the SCOPES. I will be modifying emails in a later post in the series and we also need access to the labels to be able to read and modify them.

The next set of functions will be added to the file just above: def main():

First we might want to be able to get a list of all the labels we have in our Gmail account. This can be done using this code.

def get_labels(service):
    list_of_labels = service.users().labels().list(userId='me').execute()
    return list_of_labels.get('labels')
Enter fullscreen mode Exit fullscreen mode

get_labels return a list of json objects. Which are just dictionaries in python.

Next we may want to create a new label to use with Gmail. This can be done with following code.

def define_label(name, mlv="show", llv="labelShow"):
    label = dict()
    label["messageListVisibility"] = mlv
    label["labelListVisibility"] = llv
    label["name"] = name
    return label
Enter fullscreen mode Exit fullscreen mode

This is the simplest json form that will create a valid label in Gmail.
If we call this function:

define_label("test")
Enter fullscreen mode Exit fullscreen mode

we get the following json string:

{
  'messageListVisibility': 'show',
  'labelListVisibility': 'labelShow',
  'name': 'test'
}
Enter fullscreen mode Exit fullscreen mode

After creating this object we want to actually add it to Gmail. We can do that using the following code.

def add_label_to_gmail(service, label):
    try:
        created_label = service.users().labels().create(userId='me',
                                                        body=label).execute()
        return created_label
    except Exception as e:
        logger.error(e)
Enter fullscreen mode Exit fullscreen mode

What add_label_to_gmail() does is call the API with a body of json containing the information to create a new label within Gmail. If there is a problem, for example, trying to add a new label when one with the name already exists; we will get an exception.

If everything goes well we will get a new json containing the original json we created, but it will also now contain the label's id

{
  'id': 'Label_30',
  'name': 'test',
  'messageListVisibility': 'show',
  'labelListVisibility': 'labelShow'
}
Enter fullscreen mode Exit fullscreen mode

We can now get that new id using the next function

def get_new_label_id(new_label):
    return new_label.get('id')
Enter fullscreen mode Exit fullscreen mode

This simply returns label's id as a string.

In practice this means making the following set of calls.

new_label = define_label("test")
new_label = add_label_to_gmail(service, new_label)
new_id = get_new_label_id(new_label)
Enter fullscreen mode Exit fullscreen mode

The completed code so far should look something like:

import pickle, os.path, sys
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.modify',
          'https://www.googleapis.com/auth/gmail.labels']

def get_service():
    """Shows basic usage of the Gmail API.
    Lists the user's Gmail labels.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    # Another option to ignore google cache logging issue
    # service = build('gmail', 'v1', credentials=creds, cache_discovery=False)
    service = build('gmail', 'v1', credentials=creds)
    return service

def get_labels(service):
    list_of_labels = service.users().labels().list(userId='me').execute()
    return list_of_labels.get('labels')


def define_label(name, mlv="show", llv="labelShow"):
    label = dict()
    label["messageListVisibility"] = mlv
    label["labelListVisibility"] = llv
    label["name"] = name
    return label

def add_label_to_gmail(service, label):
    try:
        created_label = service.users().labels().create(userId='me',
                                                        body=label).execute()
        return created_label
    except Exception as e:
        logger.error(e)

def get_new_label_id(new_label):
    return new_label.get('id')

def main():
    service = get_service()
    new_label = define_label("test")
    added_label = add_label_to_gmail(service, new_label)
    print(added_label)
    new_id = get_new_label_id(added_label)
    print(f"Store this id: {new_id}."
          f"You will need it again later")


if __name__ == '__main__':
    main()

Enter fullscreen mode Exit fullscreen mode

But why or how do we use this new id once its been created and added to Gmail?

As mentioned at in the first article. I am working on making a system that queries and sends notifications based on some emails. I have a need to query Gmail fairly frequently during certain times of the day, say once every 5 minutes. Gmail's search options only allows me to limit my search conditions to emails that are newer_than:1day. That means, I will see the same emails repeatedly triggering multiple notifications.

The fix? Add a label when a message is processed and then use -label:labelname in the search string. Meaning that once an email is processed. It won't get processed a second time.

How do you add a label to a message?

def add_label_to_message(service, msg_id, label_id):
    try:
        msg = service.users().messages().modify(userId='me',
                                                id=msg_id,
                                                body={'removeLabelIds': [],
                                                      'addLabelIds': [label_id]}
                                                ).execute()
    except Exception as e:
      # Do something here. print or log

Enter fullscreen mode Exit fullscreen mode

This takes the service, a msg_id, and the label_id we got after creating the new label.

Where does the msg_id come from? That is a story for the next post in our series.

Latest comments (2)

Collapse
 
camarillocorp profile image
Jesús Camarillo

This is great!!! I can work with this and other related things with no issue on local environment, but when trying to get the Auth Consent screen it just not working.

from_client_secrets_file

flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json',
scopes=['googleapis.com/auth/androidmanagem...)

creds = flow.run_local_server(
    host='localhost',
    port=8080,
Enter fullscreen mode Exit fullscreen mode

Do you have any tutorial or info on how to make it work on live ubuntu server? Thank you

Collapse
 
basman profile image
Adam.S

Hi.
Did you take a look at article one in the series? Followed the guide here: medium.com/better-programming/a-be...

Also at the end of the article there is reference to an issue that I myself had. When using my default browser, which is opened by the script, I was unable to authenticate. I had to copy the generated link in a different browser; while I was still attempting to authenticate. Check the first article, and if you still have trouble I will see if I can give you some additional help. But it's been a while since I have looked at this.