lib/helpers.js (79 lines of code) (raw):
/**
* Copyright 2018, Google LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const cheerio = require('cheerio');
const google = require('googleapis');
const gmail = google.gmail('v1');
const oauth = require('./oauth');
const pify = require('pify');
const Vision = require('@google-cloud/vision');
const visionClient = new Vision.ImageAnnotatorClient();
/**
* Get base64-encoded image attachments in a GMail message
* @param message The GMail message to extract images from
* @returns A promise containing a list of base64-encoded images
*/
const _getImageAttachments = (message) => {
// Get attachment data
const attachmentIds = message.payload.parts
.filter(x => x.mimeType && x.mimeType.includes('image'))
.map(x => x.body.attachmentId);
// Return base64-encoded images
return Promise.all(attachmentIds.map(attachmentId => {
return pify(gmail.users.messages.attachments.get)({
auth: oauth.client,
userId: 'me',
id: attachmentId,
messageId: message.id
}).then(result => {
// Convert from base64url to base64
const imageData = result.data.replace(/-/g, '+').replace(/_/g, '/');
return Buffer.from(imageData, 'base64');
});
}));
};
/**
* Get URL-referenced images in a GMail message
* @param message The GMail message to extract images from
* @returns A list of image URLs
*/
const _getImageUrls = (message) => {
const unpack = (x) => {
return Buffer.from(x.body.data || '', 'base64').toString();
};
// Get message's HTML
let rawHtml = message.payload.parts.map(
p => unpack(p)
).join('');
rawHtml += unpack(message.payload);
// Return image URLs
return cheerio.load(rawHtml)('img')
.toArray()
.map(image => image.attribs.src);
};
/**
* Get all images from a GMail message
* @param message The GMail message to extract images from
* @returns A promise containing a list of {image URLs, base64-encoded images}
*/
exports.getAllImages = (msg) => {
const urlImages = _getImageUrls(msg);
const base64Images = _getImageAttachments(msg);
return Promise.all([urlImages, base64Images])
.then(([urlImages, base64Images]) => urlImages.concat(base64Images));
};
/**
* List GMail message IDs
* @returns A promise containing a list of GMail message IDs
*/
exports.listMessageIds = () => {
return pify(gmail.users.messages.list)(
{ auth: oauth.client, userId: 'me' }
);
};
/**
* Get a GMail message given a message ID
* @param messageId The ID of the message to get
* @returns A promise containing the specified GMail message
*/
exports.getMessageById = (messageId) => {
return pify(gmail.users.messages.get)({
auth: oauth.client,
id: messageId,
userId: 'me'
});
};
/**
* Label a GMail message
* @param messageId The ID of the message to label
* @param labels The labels to apply to the message
*/
exports.labelMessage = (messageId, labels) => {
return pify(gmail.users.messages.modify)({
auth: oauth.client,
id: messageId,
userId: 'me',
resource: {
addLabelIds: labels
}
});
};
/**
* Get labels for a series of images
* @param images A list of {base64-encoded images, image URLs} to label
* @param returns A flattened list of labels for the specified images
*/
exports.getImageLabels = (images) => {
// Get labels for each images
const requests = images.map(image => visionClient.labelDetection(image));
return Promise.all(requests)
.then(results => {
// Propagate request errors (to be caught elsewhere)
if (results[0].error) {
throw new Error(results[0].error);
}
// Map to label array-of-arrays
return results.map(result =>
result[0].labelAnnotations.map(label => label.description)
);
})
.then(labelSet => {
// Flatten labelSet
return labelSet.reduce((x, y) => x.concat(y), []);
});
};