Deleting Gmail messages by label with the Google API

(0 comments)

Google's suite of APIs and their Python bindings can be used to delete emails automatically, saving the hassle of navigating page after page of 100 emails at a time. Setting up a Developer project, configuring OAuth access etc. is described on the Google Developers page here. Don't forget to add yourself as a Test User.

Warning: the code below will permanently delete all the messages labelled with the chosen label (they won't be moved to the Bin/Trash folder.

The script should be run in a virtual environment, providing the label for the emails to be deleted on the command line. The pip requirements.txt file is given at the end of this article.

May LinkedIn's sycophants and sociopaths darken your inbox no more.

import sys
import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# We need full access to delete emails
SCOPES = ["https://mail.google.com/"]
# Delete messages in batches of this size (max. 1000).
BATCH_SIZE = 1000

# Delete all messages labelled with label.
label = sys.argv[1]

# Validate the API tokens, getting them using the credentials.json if
# the file token.json doesn't exist or has expired.
# This part is from the Google API example.
if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
# 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.json", "w") as token:
        token.write(creds.to_json())

service = build("gmail", "v1", credentials=creds)

def yield_messages(label):
    """A generator function to yield messages labelled by label."""
    q = f"in:{label}"
    results = service.users().messages().list(userId="me", q=q).execute()
    messages = results.get("messages", [])
    # Get the first page of messages.
    for msg in results["messages"]:
        yield msg
    while "nextPageToken" in results:
        # As long as there are more pages of messages, yield them.
        page_token = results["nextPageToken"]
        results = (
            service.users()
            .messages()
            .list(userId="me", q=q, pageToken=page_token)
            .execute()
        )
        if "messages" in results:
            for msg in results["messages"]:
                yield msg


def delete_messages(msg_ids):
    """Batch delete all messages with ids msg_ids."""
    service.users().messages().batchDelete(userId="me",
            body={"ids": msg_ids}).execute()


msg_ids = []
ndeleted = 0
for msg in yield_messages(label):
    msg_ids.append(msg["id"])
    if len(msg_ids) == BATCH_SIZE:
        delete_messages(msg_ids)
        ndeleted += BATCH_SIZE
        msg_ids = []
        print("batch deleted.")
if msg_ids:
    # Delete the last batch of messages.
    delete_messages(msg_ids)
    ndeleted += len(msg_ids)
print(f"{ndeleted} messages deleted.")

The necessary Python packages are listed in this requirements.txt file:

black==23.7.0
cachetools==5.3.1
certifi==2023.5.7
charset-normalizer==3.2.0
click==8.1.6
google-api-core==2.11.1
google-api-python-client==2.95.0
google-auth==2.22.0
google-auth-httplib2==0.1.0
google-auth-oauthlib==1.0.0
googleapis-common-protos==1.59.1
httplib2==0.22.0
idna==3.4
mypy-extensions==1.0.0
oauthlib==3.2.2
packaging==23.1
pathspec==0.11.1
platformdirs==3.9.1
protobuf==4.23.4
pyasn1==0.5.0
pyasn1-modules==0.3.0
pyparsing==3.1.0
requests==2.31.0
requests-oauthlib==1.3.1
rsa==4.9
six==1.16.0
uritemplate==4.1.1
urllib3==1.26.16
Current rating: 5

Comments

Comments are pre-moderated. Please be patient and your comment will appear soon.

There are currently no comments

New Comment

required

required (not published)

optional

required