Code Monkey home page Code Monkey logo

gmail-autoresponder's Introduction

Gmail AutoResponder

GitHub last commit GitHub commit activity Google Apps Script Django License

Gmail AutoResponder is a full-stack web application for automated email processing.

Table of Contents

1. App Architecture

App Architecture

1.1. Backend – Core: Google Apps Script


apps-script--logo.png

The Core App is a Google Apps Script app deployed as an API executable, and managed through the Apps Script API using the Python client library.

1.1.1. App Settings

App settings are managed as Apps Script User Properties, and so are all stored as strings in key-value pairs.
Properties types here are only indicative, and often refer to their corresponding Django Forms Field classes in the Middleware App:

Property Description
IS_GSUITE_USER Boolean
enableApp Boolean. (default: 'false')
coreAppEditUrl String. Edit URL of the Apps Script project.
filters JSON string; Message content filters.

default:
{
"rawContent": [
'report-type=disposition-notification', // Read receipts
'Content-Type: multipart/report', // Automatic delivery reports
'report-type=delivery-status', // Automatic delivery reports
'Content-Type: message/delivery-status' // Automatic delivery reports
],
"from": [
'(^|<)((mailer-daemon|postmaster)@.)',
'noreply|no-reply|do-not-reply',
'.+@.
\bgoogle\.com',
Session.getActiveUser().getEmail()
],
"to": [
'undisclosed-recipients' // Potential spams
]
}
logger JSON string. identifiers property of an AppLogger class (a child class of BaseLogger) instance.

Example:
{
"id": "ABCDEF",
"viewUri": "https://www.xxxx.yy/ABCDEF?view",
"updateUri": "https://www.xxxx.yy/ABCDEF?update"
}
timeinterval Integer. (default: 10)
starthour Integer. (default: 17)
finishhour Integer. (default: 8)
utcoffset Integer. (default: 0)
ccemailadr String, one or a RFC-compliant comma-separated list of email addresses.
default: ''
bccemailadr String, one or a RFC-compliant comma-separated list of email addresses.
default: ''
noreply Boolean; whether or not to reply with a noreply@ email address.
default: 0 if IS_GSUITE_USER === 'true', 2 otherwise.
msgbody String; Response message body in HTML format.
default: getDefaultMessageBody() function return value.
testEmail String. (default: null or '')

If set to a valid email address, enableApp, starthour and finishhour will be ignored, and a test message to that address will be sent in response to any received “non-filterable-out” email.
Deleting the property or setting it to '' (or any other non-valid email value) will switch the application from its “Test Mode”.

1.1.2. Execution

When the Core App is enabled (i.e. enableApp === 'true'), main() function of the main script main.js will be continuously executed (triggered) on a recurring interval of timeinterval (+2) minutes from (starthour + utcoffset) to (finishhour + utcoffset).
On each execution, referred to as Execution n, the function issues a Gmail search query to fetch last received messages. The search query returns an array of Gmail threads that were updated in the last timeinterval (+2) minutes. The last message of each of these threads is extracted and processed, i.e. it would either be responded to or skipped if it matches one of the exclusion filters in the filters property.
A Session is a series of triggered executions within a 24 hours span (e.g. from 05-Sep-2018 @7:00pm to 06-Sep-2018 @7:00am).

In order to neither miss a message nor send an automated response more than once :

  1. Although Execution (n-1) would normally have occurred timeinterval minutes ago, received messages are fetched from the last (timeinterval + 2) minutes, in order not to miss any messages in case of a delay.
  2. IDs of processed messages from Execution (n-1) are cached with a (timeinterval + 6) minutes timeout. During Execution n, IDs of retrieved messages are checked against this cache to determine whether they were already processed or not.

1.1.3. Logging

On each execution, the following information are logged to the AppLogger class instance, which is constructed from an identifiers JSON object generated by parsing the logger Script property:

PROCESSED EXECUTIONS
Metadata of both messages responded to and those skipped (filtered out) :

  • Label (REP_SENT or SKIPPED),
  • Date/time Sent (Original Message),
  • Date/time Sent (Response) (when applicable),
  • Message ID,
  • Thread ID,
  • From,
  • Subject,
  • Applied filter (if the message was skipped).
  • Gmail search query.
  • Execution time.
  • Number of threads returned (i.e. number of search results).

The default logger is a Google Spreadsheet, but it is fairly easy to extend the BaseLogger class to create other logging targets as long as the following points are considered :

  • The database has a REST API.
  • Possibility to connect with Read/Write permissions to an existing instance (database), or create a new one if required.
  • Possibility to create two data collections: PROCESSED and EXECUTIONS (e.g.: GSpreadsheets: sheets, SQL: tables, DocumentDB: collections), each with the specified data fields (e.g.: GSpreadsheets: columns/header, SQL: columns, DocumentDB: fields).
  • Logs would then be stored as single entries or 2D datasets (e.g.: GSpreadsheets: rows, SQL: records, DocumentDB: documents).

1.2. Backend – Middleware: Django


django--logo.png

The Middleware backend component requires the Core app ID, and the Client ID credentials file (credentials.json) of the associated GCP project.

It is a Django app providing the following functionalities:

  • Authentication: User sign-in through a full OAuth2 authentication flow.
  • Sessions: Based on Django Sessions, allow users (identified by their parsed OIDC tokens) to access the webapp independently from any active Google account in the browser. No user data is kept after logout.
  • API Gateway: Aggregates calls to the Core app by providing API endpoints to initialize, retrieve and update app settings. This enables the development of client-side apps, which typically use the OAuth2 consent flow for installed applications and store both access and refresh tokens. The Middleware app here acts as a proxy, by checking HTTP Authorization headers for bearer tokens and translating requests and responses between the custom client and the Core app.
  • Data validation: Form data (App settings) validation and multi-level error handling: HTTP request, Django Forms API data validation, Apps Script API and Core App errors.
  • Handling App URLs: mapping between features, URLs and views: Home page, Login, Authentication (OAuth2 authorized redirect URI), API Gateway endpoint URLs, Getting/Updating App settings, App reset and Logout.

1.3. Frontend


frontend--logos.png

Django templates + {CSS framework}.

The Frontend part is basically a Django template providing access to all needed features: Logged-in user information on top of a form to view and update App Settings, along with Logout and App Enable/Disable/Reset commands.

2. Setup

Asciinema: Gmail AutoResponder - Dev/Test Deployment

  • Notes:
    • This section is still being actively worked on. The whole process logic is currently drafted as a Bash script you can check here along with a sample output. You can also check this asciicast.
    • Had it not been for clasp and gcloud command line tools limitations/design choices, most (if not all) of the tasks would have been fully automatable.

2.1. Provision


provision.png

Basically, all we need is a Google account and a Linux host (later referred to as the “Target Server”) with a well configured ‘deploy’ user, and optionally a fixed public IP address (if internet exposure is needed).

Install software requirements and configure development, testing and deployment environments:

  • Development machine: Git, Python3, a modern and updated web browser, text editor/IDE, SSH client, virtualization software (optional, but recommended), and—when required—remote access (typically through a web interface) to a CI/CD server.

  • Control Machine or Deployment Server: A dedicated host with SSH service installed and configured; typically with a configuration management software like Ansible, and—if need be—a CI/CD software or service, like Jenkins and GitHub Actions.

  • Target Server: Install dependencies and make required configurations:

    • Install system-wide software requirements, generally: git, curl, jq, clasp, nginx, certbot, python3 and uwsgi.
    • Setup a Google Cloud Platform (GCP) Project:
      • Note the Project Number
      • Enable required Google APIs: Apps Script API, Google Drive API, Gmail API, Google Sheets API.
      • Configure OAuth consent screen:
        • Add scopes:

          openid
          https://www.googleapis.com/auth/script.scriptapp
          https://mail.google.com/
          https://www.googleapis.com/auth/drive
          https://www.googleapis.com/auth/userinfo.email
          https://www.googleapis.com/auth/spreadsheets
          https://www.googleapis.com/auth/userinfo.profile
          
        • Create a OAuth2 Client ID for a web application. Set redirect URIs, depending on the deployment stage. Get credentials JSON file.

    • Configure Google Apps Script:
      • Enable Google Apps Script API (from the Apps Script dashboard).
      • Authorize clasp command line tool.
      • Create a new Apps Script project to be deployed as an API Executable.
      • Switch Apps Script's Google Cloud Project association from the default project to the standard (user-managed) project already created.

2.2. Configure


configure.png

Install app dependencies and make a base deployment of the backend:

  • Push and deploy Core app code (/app/core content) to the newly created Apps Script project.
  • Create and configure a Django app:
    • Install Python3 requirements (/app/middleware/requirements.txt) in a virtual environment.
    • Apply all migrations (python manage.py migrate)
  • Configure NGINX, uWSGI and Certbot (for Testing and Production deployments).

2.3. Deploy


deploy.png

Continuous Deployment (CD): versioned for the Core and incremental for the Middleware.

There are three deployment stages:

Parameters 💻 Development 🧪 Testing 🏭 Production
User [1] Apps Script project owner Any allowed Google user Any allowed Google user
devMode [2] True False False
Versioned deployment [3] No
(use SCRIPT_ID)
Yes
(use DEPLOYMENT_ID)
Yes
(use DEPLOYMENT_ID)
HTTP Server [4] Django Development Server NGinx + uWSGI
+ certbot (specifically for public cloud deployments)
NGinx + uWSGI + certbot
Test Mode [5] Yes No
Hosts [6] localhost, 127.0.0.1 - LAN: hostnames or private IP addresses of external hosts.
- Internet: [sub]domain names or public IP addresses, ideally with HTTP Basic Auth.
[sub]domain name or public IP address
AppLogger [7] Class name and identifiers.
default: GSpreadsheetLogger
Class name and identifiers.
default: GSpreadsheetLogger
Class name and identifiers.
default: GSpreadsheetLogger
Debug & Log [8] Yes OK No DEBUG mode

3. Background

I started Gmail AutoResponder back in 2017 as a script to manage automatic email responses outside the active hours of the company I worked for.
Although it was possible to set Gmail to individually send canned responses, I could neither make time-specific filters nor programmatically make Gmail trigger an event upon email reception. So, inspired by an answer on one of StackExchange forums, I had to figure out a way around and ultimately ended up with a basic Apps Script app, 6 instances of which have amazingly run for almost 3 years and processed more than 17k messages!

To see how the project progressed, check worklog.md.

4. License

This software is under the MIT license.

gmail-autoresponder's People

Contributors

amindeed avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

progrmanial

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.