index.js (96 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 google = require('googleapis'); const gmail = google.gmail('v1'); const querystring = require('querystring'); const pify = require('pify'); const config = require('./config'); const oauth = require('./lib/oauth'); const helpers = require('./lib/helpers'); /** * Request an OAuth 2.0 authorization code * Only new users (or those who want to refresh * their auth data) need visit this page */ exports.oauth2init = (req, res) => { // Define OAuth2 scopes const scopes = [ 'https://www.googleapis.com/auth/gmail.modify' ]; // Generate + redirect to OAuth2 consent form URL const authUrl = oauth.client.generateAuthUrl({ access_type: 'offline', scope: scopes, prompt: 'consent' // Required in order to receive a refresh token every time }); return res.redirect(authUrl); }; /** * Get an access token from the authorization code and store token in Datastore */ exports.oauth2callback = (req, res) => { // Get authorization code from request const code = req.query.code; // OAuth2: Exchange authorization code for access token return new Promise((resolve, reject) => { oauth.client.getToken(code, (err, token) => (err ? reject(err) : resolve(token)) ); }) .then((token) => { // Get user email (to use as a Datastore key) oauth.client.credentials = token; return Promise.all([token, oauth.getEmailAddress()]); }) .then(([token, emailAddress]) => { // Store token in Datastore return Promise.all([ emailAddress, oauth.saveToken(emailAddress) ]); }) .then(([emailAddress]) => { // Respond to request res.redirect(`/initWatch?emailAddress=${querystring.escape(emailAddress)}`); }) .catch((err) => { // Handle error console.error(err); res.status(500).send('Something went wrong; check the logs.'); }); }; /** * Initialize a watch on the user's inbox */ exports.initWatch = (req, res) => { // Require a valid email address if (!req.query.emailAddress) { return res.status(400).send('No emailAddress specified.'); } const email = querystring.unescape(req.query.emailAddress); if (!email.includes('@')) { return res.status(400).send('Invalid emailAddress.'); } // Retrieve the stored OAuth 2.0 access token return oauth.fetchToken(email) .then(() => { // Initialize a watch return pify(gmail.users.watch)({ auth: oauth.client, userId: 'me', resource: { labelIds: ['INBOX'], topicName: config.TOPIC_NAME } }); }) .then(() => { // Respond with status res.write('Watch initialized!'); res.status(200).end(); }) .catch((err) => { // Handle errors if (err.message === config.UNKNOWN_USER_MESSAGE) { res.redirect('/oauth2init'); } else { console.error(err); res.status(500).send('Something went wrong; check the logs.'); } }); }; /** * Process new messages as they are received */ exports.onNewMessage = (event) => { // Parse the Pub/Sub message const dataStr = Buffer.from(event.data.data, 'base64').toString('ascii'); const dataObj = JSON.parse(dataStr); return oauth.fetchToken(dataObj.emailAddress) .then(helpers.listMessageIds) .then(res => helpers.getMessageById(res.messages[0].id)) // Most recent message .then(msg => Promise.all([msg, helpers.getAllImages(msg)])) .then(([msg, images]) => Promise.all([msg, helpers.getImageLabels(images)])) .then(([msg, labels]) => { if (!labels.includes('bird')) { throw new Error(config.NO_LABEL_MATCH); // Exit promise chain } return helpers.labelMessage(msg.id, ['STARRED']); }) .catch((err) => { // Handle unexpected errors if (!err.message || err.message !== config.NO_LABEL_MATCH) { console.error(err); } }); };