2
votes

Im trying to make a node app on my local computer that does this:

1) Looks at my gmail inbox.

2) If an email has a certain label and an attachment, download it.

Im on step 2.

The part Im confused about is the part about 'parts'.

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

Googles sample code:

function getAttachments(userId, message, callback) {
  var parts = message.payload.parts;
  for (var i = 0; i < parts.length; i++) {
    var part = parts[i];
    if (part.filename && part.filename.length > 0) {
      var attachId = part.body.attachmentId;
      var request = gapi.client.gmail.users.messages.attachments.get({
        'id': attachId,
        'messageId': message.id,
        'userId': userId
      });
      request.execute(function(attachment) { // <--- that attachment param
        callback(part.filename, part.mimeType, attachment);
      });
    }
  }
}

It seems that 'attachment' contains the attachment data.

Here is my code:

const gmail = google.gmail({version: 'v1', auth});

gmail.users.messages.list({
  userId: 'me',
  'q':'subject:my-test has:nouserlabels has:attachment'
}, (err, res) => {
  if (err) return console.log('1 The API returned an error: ' + err);

  const msgs = res.data.messages;

  if(typeof msgs !== 'undefined'){
    console.log('Messages:');
    msgs.forEach( msg => {

      console.log(`- ${msg.id}`);

      gmail.users.messages.get({
        userId: 'me',
        id: msg.id,
        format:'full'
      },(err, res) => {
        if (err) return console.log('2 The API returned an error: ' + err);

        // Wheres the data????? //

        console.log('A')
        console.log(res.data.payload.parts)

        console.log('B')
        console.log(res.data.payload.parts[1])

        console.log('C')
        console.log(res.data.payload.parts[1].body)
      })
    });
  } else {
    console.log('no messages found')
  }


});
1
If that is really the official google example, they migjt have a look at "closures inside for loops" ... But whatsoever, whats your question?!Jonas Wilms
Basically, how to download an attachment from gmail with nodejs. Ive given what Ive tried above.Jason Lough

1 Answers

0
votes

From each message, you can use part.body.attachmentId to make a Gmail API call using gmail.users.messages.attachments.get.

Below is a function which returns a JSON object with both the attachment size and the base64 encoded attachment. I realize it is kind of messy right now, I'm still polishing it up. I left the console.logs in case you might want to inspect the objects further. You should be able to add your gmail.users.messages.list query to my function as well.

From here, you could perhaps save these using an npm package to save the attachment files, for example https://www.npmjs.com/package/base64-to-image.

function listMessagesWithAttachments(auth, callback) {
    const gmail = google.gmail({version: 'v1', auth})
    console.log('list messages with attachments auth: ' + JSON.stringify(auth))
    gmail.users.messages.list({
      userId: 'me',
      maxResults: 5
    }, (err, {data}) => {
      if (err) return console.log('Error in listMessageswithAttachments, messages list: ' + err)
      let dataForExpress = []
      let messages = data.messages
      messages.forEach(function (message, index) {
        gmail.users.messages.get({
          userId: 'me',
          id: data.messages[index].id,
          format: 'full'
        }, (err, {data}) => {
          if (err) return console.log('Error: ' + err)
            //console.log('data to push: ' + data)
            //console.log('messages.length: ' + messages.length)
            dataForExpress.push(data)
            if (dataForExpress.length == messages.length) {
              let attachmentIds = []
              let attachments = []
                dataForExpress.forEach(function(message, index) {
                    //console.log('message number: ' + index)
                    //console.log(message.payload.parts)
                    let messagePayloadParts = message.payload.parts 
                    if (messagePayloadParts.length > 0) {
                        messagePayloadParts.forEach(function (part, j) {
                        //console.log('messageId: ' + JSON.stringify(message.id))
                        //console.log('message parts partId ' + JSON.stringify(part.partId))
                        //console.log('mime-type ' + JSON.stringify(part.mimeType))
                        //console.log('filename ' + JSON.stringify(part.filename))
                            let object = {}
                            if (part.body.size && part.body.size > 0 && part.body.attachmentId) {
                                //console.log('messageId: ' + JSON.stringify(part.partId))
                                //console.log('attachmentId ' + JSON.stringify(part.body.attachmentId))
                                let object = {"index": `${j}`, "messageId": message.id, "attachmentId": part.body.attachmentId}
                                attachmentIds.push(object)
                            }
                            if (dataForExpress.length - 1 == index) {
                                attachmentIds.forEach(function (attachment, kindex) {
                                    gmail.users.messages.attachments.get({
                                        userId: 'me',
                                        messageId: attachment.messageId,
                                        id: attachment.attachmentId
                                    }, (err, {data}) => {
                                        if (err) return console.log('Error getting attachment: ' + err)
                                        //console.log('attachment' + JSON.stringify(data))
                                        attachments.push(data)
                                        if (attachmentIds.length == attachments.length){
                                        console.log('callback: ' + JSON.stringify(attachments))
                                        callback(attachments)
                                        }
                                    })
                                })   
                            }
                        })                               
                    }              
                })
            }
        })
        })
    })
}