Effortlessly Clear Unread Gmail Emails Using Scala CLI
Written on
Introduction to Scala CLI and Gmail Management
Are you feeling inundated by a sea of unread emails in your inbox? Have you ever thought about just wiping them all out and achieving that elusive inbox zero? If so, this guide is for you! Not only does it cater to those overwhelmed by emails, but it also serves as a resource for anyone interested in Scala or coding in general.
You might ask, why not simply use the Gmail interface to delete those emails? As a programmer, my curiosity led me to consider automating this process with a straightforward script. Being a long-time Scala enthusiast, I wanted to put Scala CLI to the test after coming across a blog post by Alexandru Nedelcu, who showcased its use for updating NextDNS configurations. Inspired, I decided to tackle my unread email problem using Scala CLI.
Step 1: Setting Up a Google Cloud Project
To get started, we'll utilize Google Workspace APIs alongside Scala CLI. A crucial first step is to set up a Google Cloud project. If you’re unsure how to do this, refer to this guide on project creation in Google Cloud. I decided to name my project 'google-api' as shown below:
Step 2: Activating Google Workspace API
Next, you’ll need to activate the Google Workspace APIs for your project. For detailed instructions, consult this guide on the activation process. I followed the steps and successfully enabled the APIs.
Step 3: Generating Credentials for Google Workspace APIs
The subsequent step involves creating credentials for the Google Workspace APIs. These credentials are essential for obtaining an access token from Google's authorization servers, allowing your application to communicate effectively with the APIs. You’ll need to create OAuth client ID credentials and specify the application type as 'Desktop App.' Follow this guide for a detailed walkthrough. I named my credentials "Gmail Client," as illustrated below:
Step 4: Downloading the Credentials File
After generating the credentials, download the JSON file. It's advisable to store this file securely in a temporary location. For example:
/tmp/secure/google-api-credentials.json
Step 5: Installing Scala CLI
Awesome! We’ve reached our first milestone. Now, let’s dive into Scala CLI. Developed by VirtusLab, Scala CLI is a new command-line tool that simplifies Scala interactions in various ways. It particularly shines in creating single-file scripts, eliminating the complexities of project structures and build tools like SBT. You can find installation instructions in this guide.
Step 6: Writing the Script
Once Scala CLI is installed, it’s time to write a single-file Scala script. Create a new Scala file in your working directory, naming it delete_unread_emails.scala. Inside this file, add the following script:
#!/usr/bin/env -S scala-cli
//> using dep "com.google.api-client:google-api-client:2.2.0"
//> using dep "com.google.oauth-client:google-oauth-client-jetty:1.34.1"
//> using dep "com.google.apis:google-api-services-gmail:v1-rev20220404-2.0.0"
//> using dep "com.monovore::decline::2.4.1"
//> using dep "org.typelevel::cats-core::2.10.0"
import com.google.api.client.auth.oauth2.Credential
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver
import com.google.api.client.googleapis.auth.oauth2.{GoogleAuthorizationCodeFlow, GoogleClientSecrets}
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.gson.GsonFactory
import com.google.api.client.util.store.FileDataStoreFactory
import com.google.api.services.gmail.model.{ListMessagesResponse, Message}
import com.google.api.services.gmail.{Gmail, GmailScopes}
import com.monovore.decline.*
import java.io.{File, FileInputStream, InputStreamReader}
import scala.jdk.CollectionConverters.*
import cats.implicits.*
object GMailClient {
def apply(userId: String, credentialsFilePath: String): GMailClient =
new GMailClient(
userId,
new Gmail.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
GsonFactory.getDefaultInstance,
new GoogleAuthenticator(userId, credentialsFilePath).getCredential
).build()
)
}
class GMailClient(userId: String, gmail: Gmail) {
def getUnreadMails: List[Message] = {
val response = gmail
.users()
.messages()
.list(userId)
.setMaxResults(500)
.setQ("is:unread")
def getUnreadMailsHelper(
nextPageToken: String,
unreadMails: List[Message]
): List[Message] = {
if (nextPageToken == null)
unreadMailselse {
getUnreadMailsHelper(
response.setPageToken(nextPageToken).execute().getNextPageToken,
response
.setPageToken(nextPageToken)
.execute()
.getMessages
.asScala
.toList ++ unreadMails
)
}
}
getUnreadMailsHelper(
response.execute().getNextPageToken,
response.execute().getMessages.asScala.toList
)
}
def deleteMails(mails: List[Message]): Unit = {
var mailsSize = mails.size
mails.foreach { mail =>
gmail
.users()
.messages()
.delete(userId, mail.getId)
.execute()
println(s"Deleted Mail ID: ${mail.getId}, Remaining: ${mailsSize - 1}")
mailsSize -= 1
}
}
}
class GoogleAuthenticator(userId: String, credentialsFilePath: String) {
def getCredential: Credential = {
val gsonFactory = GsonFactory.getDefaultInstance
val clientSecrets = GoogleClientSecrets.load(
gsonFactory,
new InputStreamReader(new FileInputStream(credentialsFilePath))
)
val googleAuthorizationCodeFlow =
new GoogleAuthorizationCodeFlow.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
gsonFactory,
clientSecrets,
List(GmailScopes.MAIL_GOOGLE_COM).asJavaCollection
)
.setDataStoreFactory(new FileDataStoreFactory(new File("/tmp/secure/tokens")))
.setAccessType("offline")
.build()
new AuthorizationCodeInstalledApp(
googleAuthorizationCodeFlow,
new LocalServerReceiver.Builder().setPort(8888).build()
).authorize(userId)
}
}
object DeleteUnreadEmails
extends CommandApp(
name = "deleteUnreadEmails",
header = "Deletes unread emails from Gmail",
main = {
val emailIdOpt =
Opts.option[String]("email-id", help = "Email ID of the user")
val credentialsFilePathOpt =
Opts.option[String](
"credentials-file-path",
help = "Path of Google API credentials file"
)
(emailIdOpt, credentialsFilePathOpt).mapN {
(emailId, credentialsFilePath) =>
{
val gmailClient = GMailClient(emailId, credentialsFilePath)
gmailClient.deleteMails(gmailClient.getUnreadMails)
}
}
}
)
Let me highlight some key features of the script that Scala CLI supports seamlessly.
First, the shebang at the top allows for direct execution of the script, provided Scala CLI is installed. Making the file executable is simple. Just open your terminal, navigate to the working directory, and run the following command:
chmod +x delete_unread_emails.scala
Second, the script utilizes several essential libraries such as Google APIs, cats, and decline. Unlike a standard Scala project, there’s no need for a separate build file (like build.sbt) to manage dependencies. Instead, we specify them directly in the file, as shown below:
//> using dep "com.google.api-client:google-api-client:2.2.0"
//> using dep "com.google.oauth-client:google-oauth-client-jetty:1.34.1"
//> using dep "com.google.apis:google-api-services-gmail:v1-rev20220404-2.0.0"
//> using dep "com.monovore::decline::2.4.1"
//> using dep "org.typelevel::cats-core::2.10.0"
Upon executing the script, Scala CLI will automatically fetch the specified dependencies from the repository.
Third, we’ve parameterized the script using the decline library. For successful execution, users must provide two parameters:
- email-id: The email ID linked to the Gmail account from which you want to delete unread emails.
- credentials-file-path: The local path to your credentials JSON file.
Step 7: Running the Script
To run the script, navigate to your working directory in the terminal and execute the following command:
./delete_unread_emails.scala --email-id "[email protected]" --credentials-file-path "/tmp/secure/google-api-credentials.json"
Be sure to replace "[email protected]" with your actual Gmail ID. Running this command will redirect you to your browser, prompting you to sign in to your Google account. After selecting your account, you might encounter the following screen:
Click on 'Advanced' and proceed. You will then be asked to permit access to your emails. Click 'Continue'.
Return to your terminal, and voilà! You should see console output like this confirming that your emails have been deleted:
Deleted Mail ID: abc123, Remaining: 3
Deleted Mail ID: abc124, Remaining: 2
Deleted Mail ID: abc125, Remaining: 1
Deleted Mail ID: abc126, Remaining: 0
Here's a bonus: if you execute the script again, it won’t prompt you to sign in and grant permissions anew. This is because your access token is stored locally at /tmp/secure/tokens, enabling its reuse for future access.
Now, I no longer need to rely on bash or Python; I can simply leverage my preferred language, Scala, for all my scripting tasks.
Chapter 2: Video Resources
Video 1: How to delete all unread emails in Gmail
In this video, you'll learn a step-by-step process for deleting all unread emails in your Gmail account, making your inbox management much simpler.
Video 2: How to Delete All Unread Mails in Gmail at Once
This tutorial provides a quick guide on how to efficiently delete all unread emails in Gmail at once, ensuring an organized inbox.