Search code examples
pythongoogle-apigmail-apigoogle-apis-explorer

Gmail API - Unable to list all mails by Labels


Hello and many thanks for your replies

I have begun my programming journey a couple of days ago just for fun, and because I have liked Python for a while now, but never actually took a chance to look into it or try to experiment with it.

A day ago it came across my mind what if I could download attachments from a specific set of emails by custom user labels, ie "MyEmail" or "EmailCool", and store all of the attachments that were under these labels.

I have experimented a bit with the Gmail API, and did some of the initial testing myself through: https://developers.google.com/gmail/api/v1/reference/users/messages/list but the problem being is that I can only grab and store attachments if I only specify one of the custom labels I had created ie "MyEmail" as a parameter.

If I input both of these labels as parameters, the API throws a response of resultSizeEstimate = 0, meaning as if there were no e-mails.

Is there anything wrong I am applying under the service.users().messages.list() method? Again my code works if I give one label as a parameter, but not two custom labels at the same time.

Here is my code, for your reference:

import base64
import email
import os
import requests
import google.oauth2.credentials
import pickle
import sys

from google_auth_oauthlib.flow import InstalledAppFlow
from apiclient.discovery import build
from httplib2 import Http
from apiclient import errors
from apiclient import discovery
from google.auth.transport.requests import Request


def ListMessagesWithLabels(service, user_id, label_ids):
  """List all Messages of the user's mailbox with label_ids applied.

  Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    label_ids: Only return Messages with these labelIds applied.

  Returns:
    List of Messages that have all required Labels applied. Note that the
    returned list contains Message IDs, you must use get with the
    appropriate id to get the details of a Message.
  """
  try:
    response = service.users().messages().list(userId=user_id,
                                               labelIds=label_ids,
                                               maxResults=500).execute()
    print(response)
    messages = []

    if 'messages' in response:
      messages.extend(response['messages'])

    while 'nextPageToken' in response:
      page_token = response['nextPageToken']

      response = service.users().messages().list(userId=user_id,
                                                 labelIds=label_ids,
                                                 pageToken=page_token,
                                                 maxResults=500).execute()

      messages.extend(response['messages'])

      print('... total %d emails on next page [page token: %s], %d listed so far' % (len(response['messages']), page_token, len(messages)))
      sys.stdout.flush()

    return messages

  except errors.HttpError as error:
      print('An error occurred: %s' % error)

def GetAttachments(service, user_id, store_dir, label_ids):

    """Get and store attachment from Message with given id.

    Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    msg_id: ID of Message containing attachment.
    prefix: prefix which is added to the attachment filename on saving
    """

    try:
        # email_list = ListMessagesWithLabels(GMAIL, user_id, [label_id_one,label_id_two])  # to read unread emails from inbox
        email_list = ListMessagesWithLabels(service, user_id, label_ids)

        for email in email_list:
            message_id = email['id'] # get id of individual message
            message = service.users().messages().get(userId='me', id=message_id).execute()

            for part in message['payload']['parts']:
                newvar = part['body']

                if 'attachmentId' in newvar:
                    att_id = newvar['attachmentId']
                    att = service.users().messages().attachments().get(userId=user_id, messageId=message_id, id=att_id).execute()
                    data = att['data']
                    file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))
                    print(part['filename'])
                    path = ''.join([store_dir, part['filename']])
                    f = open(path, 'wb')
                    f.write(file_data)
                    f.close()

    except Exception as error:
        print('An error occurred: %s' % error)




# Setup the Gmail API
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly', ]
CLIENT_SECRETS_FILE = 'client_secret.json'
mail_labels = ['Label_3679404043833618922', 'Label_7190264889161813326']

workingDir = os.environ['USERPROFILE']

gmailDownloadDir = os.path.join(workingDir, 'GmailDownload/')
attachDir = os.path.join(gmailDownloadDir, 'attachments/')
cred_file = os.path.join(gmailDownloadDir, 'credentials.token')
refreshtoken_file = os.path.join(gmailDownloadDir, 'refresh.token')

if 'GmailDownload' not in os.listdir(workingDir):
    os.mkdir(gmailDownloadDir)

if 'attachments' not in os.listdir(gmailDownloadDir):
    os.mkdir(attachDir)


# Get Gmail Credentials
if not os.path.exists(cred_file):
    flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
    credentials = flow.run_local_server()

    with open(cred_file, 'wb') as token:
        pickle.dump(credentials, token)
else:
    with open(cred_file, 'rb') as token:
        credentials = pickle.load(token)

if credentials.expired:
    credentials.refresh(Request())

service = discovery.build('gmail','v1',credentials=credentials)

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


GetAttachments(service, user_id='me', label_ids=mail_labels, store_dir=attachDir)

Solution

  • If you look at the link you posted, by labelIds[] it says

    Only return messages with labels that match all of the specified label IDs.

    So the email must have all labels in the list—not just any label.

    An alternative is to use the q parameter with with something like label:MyEmail or label:EmailCool or ...

    Check out this page: https://support.google.com/mail/answer/7190