How to read a gmail email body with Go?


This afternoon I ran across a rather interesting question on Stack Overflow Gmail API shows empty body when getting message. The author of the question was trying to parse the raw email body from a Gmail get response but the raw field was always returning null.

It didn’t take me long to realize that the issue was that they were using Format(“full”) instead of Format(“raw”) which was most likely the cause of their issues. I have not written many Go tutorials, so I have to admit this took me about an hour to put together. The main reason being that the author of the question did not create a valid How to create a Minimal, Reproducible Example which meant I would need to code everything including the authorization myself.

Google Workspace service account with Gmail

In order to access Gmail api I decided to just use a sample I already had for Service accounts and Google calendar. This would mean that I needed to set up domain wide delegation to a user on my workspace domain.

The first thing I have done is Load the service account key file, once that is loaded I can create my service object. Now remember I am connecting to gmail though a google workspace domain account, this means that I need to delegate to a user on my domain, I did this by setting config.Subject property. Then I can create my service object.

ctx := context.Background()
serviceAccountJSON, err := ioutil.ReadFile(ServiceAccount)
if err != nil {
   log.Fatalf("Warning: Unable to load service account key file %v", err)
}

config, err := google.JWTConfigFromJSON(serviceAccountJSON, SCOPE)
if err != nil {
   log.Fatalf("Warning: Unable to create gmail Client %v", err)
}

config.Subject = "[Redacted]@daimto.com"

srv, err := gmail.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx)))

if err != nil {
   log.Fatalf("Warning: Unable to create drive Client %v", err)
}

List Messages

Once I have a service object i can run the user.messages.list method which will list all the messages on my deligated users gmail acocunt.

r, err := srv.Users.Messages.List("me").Do()
if err != nil {
log.Fatalf("Unable to retrieve messages: %v", err)
}
if len(r.Messages) == 0 {
fmt.Println("No messages found.")
return
}
fmt.Println("Labels:")
for _, l := range r.Messages {
fmt.Printf("- %s\n", l.Id)
GetEmail(srv, l.Id)
}

Get Message

Now for the fun part. I have created a function which will get the message and print out the raw data. In order to get the raw data we need to use Format(“RAW”) and then base64 decode it.

func GetEmail(srv *gmail.Service, messageId string) {

   gmailMessageResposne, err := srv.Users.Messages.Get("me", messageId).Format("RAW").Do()
   if err != nil {
      log.Println("error when getting mail content: ", err)
   }

   if gmailMessageResposne != nil {

      decodedData, err := base64.RawURLEncoding.DecodeString(gmailMessageResposne.Raw)

      if err != nil {
         log.Println("error b64 decoding: ", err)
      }
      fmt.Printf("- %s\n", decodedData)
   }
}

Conclusion

When posting questions on Stack Overflow please give me a full example so that I dont have to go though the process of creating everything manually. ……………. Oh never mind this was fun even if I did have to code the authorization myself.

As you can see its relatively easy to read the raw message data from gmail api using Go. Its just a matter of getting the format right.


About Linda Lawton

My name is Linda Lawton I have more than 20 years experience working as an application developer and a database expert. I have also been working with Google APIs since 2012 and I have been contributing to the Google .Net client library since 2013. In 2013 I became a a Google Developer Experts for Google Analytics.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.