[GO] Get mail using Gmail API in Java

Introduction

I tried using Gmail's API in Java, but even if I googled it, I was sad that only Stack Overflow and English articles on Google's official website came out, so I had to write an article in Japanese. thought.

Thing you want to do

I want to get the body and attachments of an email received on Gmail using Java.

Premise

--I use Eclipse. --Prepare a Google account.

Email you want to get

This is a sample email to get this time. 19.png I have attached an image of a Linux penguins in the attached file.

Import library

First, in preparation for using the Gmail API, get the Gmail API library from the following URL. Gmail API Client Library for Java ; https://developers.google.com/api-client-library/java/apis/gmail/v1

If you are using MAVEN and GRADLE, you can install the library by following the instructions at the link. If you can't use such a convenience for various reasons, download it as a zip and include all the jar files inside.

Authentication settings

Do the following two things.

--Create a project on your Google account. --Enable the Gmail API on your Google account. --Since the Gmail API uses OAuth 2.0 for authentication, prepare an OAuth 2.0 client on your Google account.

Create a project

Access the following URL and create a project. Resource Management; https://console.developers.google.com/cloud-resource-manager __1. Click "Create Project" __ 13.png

__2. Give a suitable project name and click "Create" __ 14.png

__3. A project will be created after a while __ Let's wait a moment 15.png

Enable Gmail API

Access Google's API library from the following URL and enable the Gmail API. API Library; https://console.developers.google.com/apis/library 6.png

Prepare OAuth 2.0 client

Access the following URL and create an OAuth 2.0 client. Credentials; https://console.developers.google.com/apis/credentials

__1. Click Create Credentials __ 7.png

__2. Select OAuth Client ID __ 8.png

__3. Select "Other", give it an appropriate name, and click "Create" __ 9.png

__4. Completion __ 11.png

__5. Download the private key for later use __ Anything is fine, but let's save it as "client_secret.json". 12.png

__6. Create OAuth consent screen appropriately __ 10.png

Let's write Java

The processing flow is roughly as follows, so we will proceed in order.

  1. Authentication
  2. Get message ID
  3. Get email
  4. Body decoding
  5. Download attachments

Authentication

Based on the private key obtained earlier, perform authentication work to obtain a token for using the API and a refresh token for reissuing the token.

__1. Place the previously obtained private key "client_secret.json" in the project __ 16.png

__2. I get the information necessary for authentication such as refresh token based on the private key, but create a folder in the project to save it __ For the time being, name it "credentials". 17.png

__3. Finally coding __ Get a token to use the API and a refresh token to reissue the token based on the private key

//Json processing
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
//scope
List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
//Private key
String CLIENT_SECRET_DIR = "client_secret.json";
//Storage folder for configuration files around authentication
String CREDENTIALS_FOLDER = "credentials";
//HTTP communication
NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

//Authentication
//Reading the private key
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
//Authentication settings
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
        clientSecrets, SCOPES)
                .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
                .setAccessType("offline").build();
//Authenticate
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");

First of all, explanation of constants

--JSON_FACTORY_: Object for processing JSON. The data returned by the API is in JSON format. --SCOPES: How much access should you allow to Gmail using the API? There should be all the constants in the GmailScopes class, so choose the appropriate one and use it. If you select GMAIL_READONLY, you should be able to do everything you want to do this time, so let's select it. --CLIENT_SECRET_DIR: The path of the private key file. This time, I put the name "client_secret.json" directly under it, so "client_secret.json" is OK. --CREDENTIALS_FOLDER: The path of the folder to save the files around the authentication obtained by using the private key. Set the folder created above. --HTTP_TRANSPORT: Object for HTTP communication.

Next, explanation of processing

//Reading the private key
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);

I'm reading the private key "client_secret.json" directly underneath

GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
    .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
    .setAccessType("offline").build();

I put the setting to throw the authentication.

--new GoogleAuthorizationCodeFlow.Builder argument --__ HTTP communication object __: HTTP_TRANSPORT --_JSON processing object : JSON_FACTORY -- private key : clientSecrets -- Scope : SCOPES (the one set to READ ONLY) --SetDataStoreFactory argument -- Output destination of files around authentication obtained using private key __ --setAccessType argument --_Offline access __ (not human access on the browser), so use "offline"

//Authenticate
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");

Actually perform HTTP communication, throw the private key, get the token or access token, and save it in the specified folder. When you run it, you should see some files in the folder as shown below. 18.png

Get message ID

I use an API called list of messages. messages is an API for getting, sending, and manipulating emails, and list literally gets a list of received emails. Since it is just a list, you can only get the message ID and thread ID, and you cannot see the text. Use get in messages to get the text and detailed data. For the time being, the explanation of the official website is as follows (American)

Users.messages: list ; https://developers.google.com/gmail/api/v1/reference/users/messages/list

It is convenient to read because it contains the information required to throw the API, the required scope, the JSON definition to be returned, and sample code in various languages. It's American.

The first thing to do is to create an object for the Gmail service, execute the messages list on that object, and put the returned data into the List.

//Create a Gmail service
//Application name
String APPLICATION_NAME = "Test1";
//Access Gmail
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();

//Get email
//User
String USER = "me";
//Get all emails(Only the message ID is included)
ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
List<Message> messageList = messagesResponse.getMessages();

Description of constants

--APPLICATION_NAME: The name to give to the Gmail service object you create. Appropriate.

Description of processing

//Create a Gmail service
//Application name
String APPLICATION_NAME = "Test1";
//Access Gmail
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();

I'm creating an object for a Gmail service.

--New Gmail.Builder argument --__ HTTP communication object __: HTTP_TRANSPORT --_JSON processing object : JSON_FACTORY -- Credentials __: credential


//Get email
//User
String USER = "me";
//Get all emails(Only the message ID is included)
ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
List<Message> messageList = messagesResponse.getMessages();

I run a list of messages and store the results in a List. The argument is user only. I want to see my email, so just put "me" in it. This will create a JSON List that contains only the message ID and thread ID, as shown below.

{
    "id": "1635e36ac0cf27ce",
    "threadId": "1635e36ac0cf27ce"
}

Get email

Further obtain detailed data such as subject and body from the message ID to narrow down the target emails.

//Explore all email data to find the email in question
Message targetMessage = null;
for (Message message : messageList) {
    //Get all email data
    Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
    //View header
    for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
        //Name is Subject in the header(subject)Get the message ID of the message whose Value is "test mail"
        if (header.getName().equals("Subject") && header.getValue().equals("Test email")) {
            targetMessage = fullMessage;
        }
    }
}

To make an excuse first, this code is not very well written, and it is better to divide this process into methods and return when the target email is found, and I would definitely do it. I will do so. This time, as a sample code to be published on the net, I wanted to write all the processing in the same method, so I did this.

Aside from excuses, what I'm doing is using the message ID from the list of retrieved Messages and using the Gmail API's get API to get more detailed data such as the subject and body. I am. We are looking for an email with the subject "test email" to find the target email in the acquired data.

Variable description

--Message targetMessage: Variable for storing the target email

Description of processing

//Get all email data
Message fullMessage = service.users().messages().get(USER, message.getId()).execute();

For the time being, the official explanation of get for messages is as follows Users.messages : get; https://developers.google.com/gmail/api/v1/reference/users/messages/get

If you put the user and message ID in the argument and throw it, more detailed mail information will be returned.

{
    "historyId": "212543",
    "id": "1635e36ac0cf27ce",
    "internalDate": "1526294031000",
    "labelIds": [
        "IMPORTANT",
        "Label_8",
        "SENT"
    ],
    "payload": {
        "body": {
            "size": 0
        },
        "filename": "",
        "headers": [
            {
                "name": "MIME-Version",
                "value": "1.0"
            },
            {
                "name": "Received",
                "value": "by 10.179.15.206 with HTTP; Mon, 14 May 2018 03:33:51 -0700 (PDT)"
            },
            {
                "name": "Date",
                "value": "Mon, 14 May 2018 19:33:51 +0900"
            },
            {
                "name": "Delivered-To",
                "value": "[email protected]"
            },
            {
                "name": "Message-ID",
                "value": "<CAJsBAS7wAJm+gaUETbUzR1fyshn=zgu4mb5sexdKAv7+jHbmCA@mail.gmail.com>"
            },
            {
                "name": "Subject",
                "value": "Test email"
            },
            {
                "name": "From",
                "value": "Fuga Hoge <[email protected]>"
            },
            {
                "name": "To",
                "value": "[email protected]"
            },
            {
                "name": "Content-Type",
                "value": "multipart/mixed; boundary=\"0000000000009360f1056c2805b7\""
            }
        ],
        "mimeType": "multipart/mixed",
        "partId": "",
        "parts": [
            {
                "body": {
                    "size": 0
                },
                "filename": "",
                "headers": [
                    {
                        "name": "Content-Type",
                        "value": "multipart/alternative; boundary=\"0000000000009360ec056c2805b5\""
                    }
                ],
                "mimeType": "multipart/alternative",
                "partId": "0",
                "parts": [
                    {
                        "body": {
                            "data": "5pys5paH44G744Gr44KD44KJ44KJDQo=",
                            "size": 23
                        },
                        "filename": "",
                        "headers": [
                            {
                                "name": "Content-Type",
                                "value": "text/plain; charset=\"UTF-8\""
                            },
                            {
                                "name": "Content-Transfer-Encoding",
                                "value": "base64"
                            }
                        ],
                        "mimeType": "text/plain",
                        "partId": "0.0"
                    },
                    {
                        "body": {
                            "data": "PGRpdiBkaXI9Imx0ciI-5pys5paH44G744Gr44KD44KJ44KJPGJyPjwvZGl2Pg0K",
                            "size": 48
                        },
                        "filename": "",
                        "headers": [
                            {
                                "name": "Content-Type",
                                "value": "text/html; charset=\"UTF-8\""
                            },
                            {
                                "name": "Content-Transfer-Encoding",
                                "value": "base64"
                            }
                        ],
                        "mimeType": "text/html",
                        "partId": "0.1"
                    }
                ]
            },
            {
                "body": {
                    "attachmentId": "ANGjdJ_kQ0lQ3VjL86PDW2-qlN7bEQf-PLcRtzgFKvkg49Mi7KGwxlS8G2sn0LcOqdjM3D9CkVyVhmu1xbdDrxJC-GaaU7cBTUr4KUbwtENCONY9p0WW0stx8QuCEF5fnC2ZkKBFjOTxLqH3QJn88RGz-1IaE8l2wdexLgCP7MhYRNVx5Q0UCuvykLJO2WU3_uyJZWyZoVz5SxJz7_kXtLLv7X58p24HE3UU3Bxi3WIp5wpznsg1Z5GOmkYNTe29f7ag52B7L0p5SS_hIeGd73WqkgkH_Hma7Qmn0fPgd6wu_HUT8sqITt96CTfrl0M",
                    "size": 124626
                },
                "filename": "linux.png ",
                "headers": [
                    {
                        "name": "Content-Type",
                        "value": "image/png; name=\"linux.png\""
                    },
                    {
                        "name": "Content-Disposition",
                        "value": "attachment; filename=\"linux.png\""
                    },
                    {
                        "name": "Content-Transfer-Encoding",
                        "value": "base64"
                    },
                    {
                        "name": "X-Attachment-Id",
                        "value": "f_jh6421y31"
                    }
                ],
                "mimeType": "image/png",
                "partId": "1"
            }
        ]
    },
    "sizeEstimate": 171687,
    "snippet": "Body Honyara",
    "threadId": "1635e36ac0cf27ce"
}

It's quite long like this.

//View header
for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
    //Name is Subject in the header(subject)Get the message ID of the message whose Value is "test mail"
    if (header.getName().equals("Subject") && header.getValue().equals("Test email")) {
        targetMessage = fullMessage;
    }
}

There is data with the key "Subject" in the header of the acquired JSON payload, and that is the subject. I'm looking for someone whose subject is "test email".

Body decoding

Some of the acquired JSON has a key of "snippet" and a value like that, "Body Honyara", but this is a snippet instead of the body, and when the list of emails is displayed Besides, it is different because it is the data of the one who displays the text a little to inform the contents. Since the body of this test email is short, it contains the full text, but basically it is better to think that the full text is not included.

20.png _ This is a snippet _

The real text is deeper in the payload Parts and is Base64 encoded, so it must be decoded into UTF8 etc. for readability.

//Acquisition of text
//Acquisition of text(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
//Decode from Base64(byte string)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
//Convert from byte string to String
String body = new String(bodyBytes, "UTF-8");

Description of processing

//Acquisition of text(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();

As you can see, the text is in an incomprehensible deep position. See the raw JSON above to see why this happened. Since the return class of the get method is Object type, I managed to get the data in the expected format by doing toString (). By the way, it is BASE64 encoded.

//Decode from Base64(byte string)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);

Convert from BASE64 to UTF8 byte string for the time being. I want you to become a String soon.

//Convert from byte string to String
String body = new String(bodyBytes, "UTF-8");

Now it's finally a String. This is the text.

Download attachments

Finally, download the attached file.

//Get attachments
//Explore Payload Part
for (MessagePart part : targetMessage.getPayload().getParts()) {
    String fileName = part.getFilename();
    //File name is null or""If not, it is an attached file, so download it.
    if (fileName != null && fileName.length() > 0) {
        //Download file(Base64)
        MessagePartBody attachPart = service.users().messages().attachments()
                .get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
        //Decode from Base64
        byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
        //Save to system
        FileOutputStream fileOutFile = new FileOutputStream(fileName);
        fileOutFile.write(fileByteArray);
        fileOutFile.close();
    }
}

For attachments, use the Gmail API message.attachments.get. For the time being, the explanation of the official website is as follows. You can get a BASE64-encoded attachment with the attachment ID and message ID.

Users.messages.attachments: get ; https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get

Description of processing

//Get attachments
//Explore Payload Part
for (MessagePart part : targetMessage.getPayload().getParts()) {
    String fileName = part.getFilename();
    //File name is null or""If not, it is an attached file, so download it.
    if (fileName != null && fileName.length() > 0) {
        //abridgement
    }
}

The file name and attachment ID are stored in the parts of the payload. However, there is also a text etc. in the same hierarchy, in which case the fileName should be "" etc., so avoid them and get the attachment ID.

//Get attachments
//Explore Payload Part
if (fileName != null && fileName.length() > 0) {
    //Download file(Base64)
    MessagePartBody attachPart = service.users().messages().attachments().get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
    //Decode from Base64
    byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
    //Save to system
    FileOutputStream fileOutFile = new FileOutputStream(fileName);
    fileOutFile.write(fileByteArray);
    fileOutFile.close();
}

Now that you have the attachment ID, use it to get the attachments using the Gmail API message.attachments.get. Since the acquired attachment file is BASE64 encoded, it is decoded into a byte string. Then save the last decoded byte string to a file. When you execute it, the attached file should be downloaded directly under the project as shown below. 21.png

at the end

If you connect all the code written here, it looks like the following.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

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;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.ListMessagesResponse;
import com.google.api.services.gmail.model.Message;
import com.google.api.services.gmail.model.MessagePart;
import com.google.api.services.gmail.model.MessagePartBody;
import com.google.api.services.gmail.model.MessagePartHeader;

public class Test {
    public static void main(String[] args) throws IOException, GeneralSecurityException {
        //Json processing
        JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
        //scope
        List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
        //Private key
        String CLIENT_SECRET_DIR = "client_secret.json";
        //Storage folder for configuration files around authentication
        String CREDENTIALS_FOLDER = "credentials";
        //HTTP communication
        NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

        //Authentication
        //Reading the private key
        FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
        InputStreamReader reader = new InputStreamReader(in);
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
        //Authentication settings
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
                clientSecrets, SCOPES)
                        .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
                        .setAccessType("offline").build();
        //Authenticate
        Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");

        //Create a Gmail service
        //Application name
        String APPLICATION_NAME = "Test1";
        //Access Gmail
        Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME)
                .build();

        //Get email
        //User
        String USER = "me";
        //Get all emails(Only email ID is included)
        ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
        List<Message> messageList = messagesResponse.getMessages();

        //Explore all email data to find the email in question
        Message targetMessage = null;
        for (Message message : messageList) {
            //Get all email data
            Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
            //View header
            for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
                //Name is Subject in the header(subject)Get the email ID of the message whose Value is "test email"
                if (header.getName().equals("Subject") && header.getValue().equals("Test email")) {
                    targetMessage = fullMessage;
                }
            }
        }

        //Acquisition of text
        //Acquisition of text(Base64)
        String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
        //Decode from Base64(byte string)
        byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
        //Convert from byte string to String
        String body = new String(bodyBytes, "UTF-8");
        //Standard output of text
        System.out.println(body);

        //Get attachments
        //Explore Payload Part
        for (MessagePart part : targetMessage.getPayload().getParts()) {
            String fileName = part.getFilename();
            //File name is null or""If not, it is an attached file, so download it.
            if (fileName != null && fileName.length() > 0) {
                //Download file(Base64)
                MessagePartBody attachPart = service.users().messages().attachments()
                        .get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
                //Decode from Base64
                byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
                //Save to system
                FileOutputStream fileOutFile = new FileOutputStream(fileName);
                fileOutFile.write(fileByteArray);
                fileOutFile.close();
            }
        }
    }
}

With this, you will be able to automatically retrieve Gmail emails, and you may be happy to be able to automate the tasks that accompany it. The Gmail API explained carefully here is also at the following URL, and although the official language is American, it explains how to use it, so if you read it, you can do anything.

API Reference ; https://developers.google.com/gmail/api/v1/reference/

As a personal impression using the Gmail API, it was troublesome to search the payload and parts when getting the subject and body, so the Message class did its best and had a lot of fields. I'm glad if you can join them automatically.

Recommended Posts

Get mail using Gmail API in Java
Get Youtube data in Python using Youtube Data API
Get image URL using Flickr API in Python
Get LEAD data using Marketo's REST API in Python
Create Gmail in Python without API
Get Amazon data using Keep API # 1 Get data
I tried using docomo speech recognition API and Google Speech API in Java
Mouse operation using Windows API in Python
Try using the Wunderlist API in Python
Get Suica balance in Python (using libpafe)
Tweet using the Twitter API in Python
Get Google Fit API data in Python
Golang api get
Email attachments using your gmail account in python.
[Python] Get all comments using Youtube Data API
Try using the BitFlyer Ligntning API in Python
Get Google Cloud Storage object list in Java
Calling CloudWatch API (using in blackbird AWS plugin)
Let's judge emotions using Emotion API in Python
How to get article data using Qiita API
Get Gmail subject and body with Python and Gmail API
To automatically send an email with an attachment using the Gmail API in Python
Try using ChatWork API and Qiita API in Python
Try using the DropBox Core API in Python
[Rails] How to get location information using Geolocation API
Get the file name in a folder using glob
Upload JPG file using Google Drive API in Python
Initial settings when using the foursquare API in python
[Python] Get insight data using Google My Business API
Send and receive Gmail via the Gmail API using Python
Try to delete tweets in bulk using Twitter API
OpenVINO using Inference Engine Python API in PC environment
Get mail from Gmail and label it with Python3
Using the National Diet Library Search API in Python
Get tweets with arbitrary keywords using Twitter's Streaming API
Facade pattern in Java
Flyweight pattern in Java
Observer pattern in Java
Linux permissions in Java
Use DataFrame in Java
Iterator pattern in Java
Evernote API in Python
Decorator pattern in Java
Get date in Python
Draft Gmail in Python
C API in Python 3
Prototype pattern in Java
Send Gmail in Python
Proxy pattern in Java
Get your heart rate from the fitbit API in Python!
Get YouTube Live chat field in real time with API
Predict gender from name using Gender API and Pykakasi in Python
Get Python scripts to run quickly in Cloud Run using responder
Get product name and lowest price using Amazon Product Advertising API
Get the weather using the API and let the Raspberry Pi speak!
Call github api in python to get pull request information