Skip to content

Latest commit

 

History

History
1459 lines (1170 loc) · 126 KB

worklog.md

File metadata and controls

1459 lines (1170 loc) · 126 KB

Work Log

2023-12-29: TODO

  • Setup (1/2):
    • Bash: Configure NGINX, WSGI and Certbot.
    • Finish Bash script and/or port to a Python script.
    • Unit tests.
    • Draft a simple CI/CD pipeline.
  • Middleware (1/2):
    • Form data validation.
    • Better error handling.
  • Frontend:
    • Complete form fields and commands
    • Responsive (Bootstrap)
    • Thoroughly test AJAX requests
  • Setup (2/2):
    • Design as a pipeline with inputs and outputs, covering all deployment stages.
    • Automate configuration and deployment
    • Deploy as a Docker image.
  • Middleware (2/2):
    • Implement API gateway. Test full workflow (e.g. with Postman).
  • Core:
    • Testing (with Middleware and frontend).
    • 1 or 2 more BaseLogger sub-classes.
    • query() method for BaseLogger class.
  • README:
    • main() flowchart
    • Setup pipeline diagram

2021-04-28

2021-04-10

2021-04-08

2021-04-05

2021-04-03

Been developing, enhancing and thoroughly testing a setup bash script: setup_centos7__draft.sh.

2021-03-30

2021-03-28

  • Been Drafting and pseudo-coding the steps of the Setup & Run (Provision, Configure and Deploy) process in a separate temporary file (setup__draft.sh). Testing on a CEntOS 7 VM.
  • Restructured the repo.

2021-03-27

2021-03-25

Drafting the "Setup and Run" part of the README and updating code accordingly.

2021-03-21

  • Been thoroughly testing Core app features.
  • Added and tested/verified more default rawContent email filters (for automatic Delivery Reports):
    • Content-Type: multipart/report
    • report-type=delivery-status
    • Content-Type: message/delivery-status
  • Temporarily removed webapp key from appsscript.json.

2021-03-20

  • Added TestApp deployment option, by providing a test email address passed as a second argument to the initSettings() function, which will enable the app right after the initialization process, but will start sending test automatic responses to the provided email address instead of the original senders.
  • Added a wrapper function autoReply() which will either call replyTest() or replyToThread(), depending on the value and validity of the testEmail script user property.
  • checkSpreadsheetById(id) method of the GSpreadsheetLogger now additionally checks if the Spreadsheet is trashed.
  • raw_notes.md updated and renamed TODO.md for convenience, as a number of issues have already been addressed.
  • Some main.js refinements.

2021-03-18

An entirely revised README, along with an App architecture diagram and some minor code modifications.

2021-03-16

  • Further refactoring and modularization of Apps Script code; Core app is now pretty stable (as far as I can tell). Key updates:
    • Class-based implementation of application loggers. Thus, we'll avoid hard-coding features and make future implementations of other logs databases easier. For instance, initializing a logs database would generally mean:
      • Connect with Read/Write permissions to an existing instance, or create a new one.
      • Create two data collections (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).
    • Revised list of App settings:
      • 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'],
            "from": [
                '(^|<)((mailer-daemon|postmaster)@.*)',
                'noreply|no-reply|do-not-reply',
                '.+@.*\\bgoogle\\.com',
                Session.getActiveUser().getEmail()
                ],
            "to": ['undisclosed-recipients']
        }
      • logger: JSON string. identifiers property of an AppLogger class 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).

    • Logical and functional organization of JavaScript libraries/files for better code reusability.
  • Added license file (MIT License).
  • Noting some interesting resources:

2021-03-11

A massive refactoring of the whole core/Apps Script code: the code is now cleaner and more modularized. A significant number of tests have been made along the way, but runtime (system/end-to-end) tests are still needed:

  • Several data parsing, serializing and validation features.
  • Code.js file and autReply() function have been respectively renamed main.js and main().
  • These functions replaced blocks of code in main.js (formerly Code.js): filterMessage(gmailMessage, filters), getLastMessage(gmailThread), replyToThread(gmailThread), appLogger(logEntries, target).
  • Core app functions are now split into three files (libraries): gmail-autoresponder.js, loggers.js and filters.js.
  • Message filters are now stored as serialized JSON objects into Script user properties.
  • Added a wrapper function appLogger() that can target (given the right parameters) log databases other that Google Spreadsheets (like document DBs accepting HTTP requests as queries...).
  • Final list of App Settings that will be stored as Script user properties:
    • firstTimeRun: Boolean (instead of INIT_ALREADY_RUN)
    • IS_GSUITE_USER: Boolean
    • enableApp: Boolean
    • filters: JSON string: default:
      {
          "rawContent": ['report-type=disposition-notification'],
          "from": [
              '(^|<)((mailer-daemon|postmaster)@.*)',
              'noreply|no-reply|do-not-reply',
              '.+@.*\\bgoogle\\.com',
              Session.getActiveUser().getEmail()
              ],
          "to": ['undisclosed-recipients']
      }
      
    • logs: JSON string: example:
      {
          "type": "gspreadsheet",
          "identifiers": 
              {
                  "id": "XXXXXXXXXXXX",
                  "url": "https://docs.google.com/spreadsheets/d/XXXXXXXXXXXX/edit#gid=0"
              }
      }
      
    • starthour: Integer
    • finishhour: Integer
    • utcoffset: Integer
    • ccemailadr: String, comma-separated email addresses (RFC-compliant)
    • bccemailadr: String, comma-separated email addresses (RFC-compliant)
    • noreply: Boolean
    • msgbody: String, HTML/Text

2021-03-07

Core (Apps Script) code is still broken, as it is being refactored:

  • Removed Google Sheets formatting code from app/core/Code.js.
  • Updated functions: getSettings() and setProperties(objParams), of app/core/gmail-autoresponder.js.
  • Renamed script user properties, to keep the same names between core, backend and frontend parts of the code.
  • Converting blocks of code to reusable functions.
  • Exploring possible ways to modularize logging features (of executed sessions and processed messages), by abstracting format (JSON...), log entry data structure and target (Google Sheets, store to a Document DB through a JSON POST request...). I'm thinking of the following inheritance mechanism of both SessionLogger and ProcessedMessageLogger objects, from a parent object AppLogger. Intended structure / Pseudo-code (check next worklog entry for update) :
    • AppLogger (Parent Object): date, append(entry|arrayOfEntries), getAllEntries(), target = {}
      • SessionLogger (Child Object): dateExecuted = super.date, numberOfThreads, append(entry|arrayOfEntries) = super.append(), getAllEntries() = super.getAllEntries(), target = super.target
      • ProcessedMessageLogger (Child Object): dateReceived = super.date, sentRepDate, messageId, threadId, messageFrom, messageSubject, appliedFilter, append(entry|arrayOfEntries) = super.append(), getAllEntries() = super.getAllEntries(), target = super.target.

2021-03-03

  • Using Django Forms as a "form data validation middleware":
    • Significant revision of core (Apps Script), backend (Django views) and frontend (Django templates) code.
    • Multi-level error handling for form submit via AJAX POST request:
      • HTTP request
      • Django Forms data validation
      • Apps Script API
      • Apps Script core app

2021-03-01

  • Significant code refinement, and even refactoring, on both core (Apps Script) and backend (Python/Django) parts:
    • Enhanced error handling when loading settings from Apps Script app using AJAX POST requests and Django Forms.
    • Removed non used functions from app\core\gmail-autoresponder.js.
  • Will come back later with more details about what has been enhanced so far. Code is still partially broken at the moment of writing this worklog entry; I just wanted to save the changes.

2021-02-24

  • Exploring Django Forms and HTML form submit using AJAX POST requests.
  • Some Apps Script code refinement.
  • Revised project repository structure (got rid of unneeded code).

2021-02-21

  • Now the Django app can load settings from Apps Script backend into the frontend, through AJAX requests.


    django-ajax-load-settings.gif

  • Handling access to some special URLs like /auth and /getsettings.

  • Refactoring views.py to use a function decorator @check_user_session to check whether the user is authenticated or not.

2021-02-18

  • All of these scopes are required if you want to get full information about the logged in Google user:
    • https://www.googleapis.com/auth/userinfo.email
    • https://www.googleapis.com/auth/userinfo.profile
    • openid
  • Customizing 'Views' to add an endpoint URL for Ajax requests (with JSON responses).

2021-02-15 (code)

  • Added working demo code (app/backend-python-demo) of a Django app that runs a function of a Google Apps Script project deployed as API-Executable, through the Google Apps Script API, using Google API Python client. Also included target Apps Script project code (app/backend-python-demo/target_appsscript_code.js)
  • The code is built upon the AuthLib library demo for Django and the Google Apps Script API Python Quickstart example:
    • I dropped the AuthLib library after many tries, due to confusing instructions about OAuth2 refresh token support for Django:
    • Analyzing the original Django demo code (that has been later modified to use google-api-python-client and google-auth-oauthlib instead of authlib):
      • Wrap up: The Django app will act as a API Gateway, so we don't need persistent storage of user information or account. The app will have access to Google user's resources until he logs out, or his session is expired or invalidated. After that, all existing data (i.e. browser cookies and session data in the database) is removed.
      • 'Diffing' changes between the default code of newly created Django app 'project' and that of the AuthLib Django demo:
        • project/settings.py (modified):
          • Removed from default configuration:
            • INSTALLED_APPS:
              • 'django.contrib.admin'
              • 'django.contrib.staticfiles'
            • MIDDLEWARE: (only SessionMiddleware was kept, which is enough for the intended use case.)
              • 'django.contrib.auth.middleware.AuthenticationMiddleware'
              • 'django.contrib.messages.middleware.MessageMiddleware'
        • project/urls.py (modified):
          • Understandably, all things related to the django.contrib.admin app were removed. Here is the full content of project/urls.py :
            # from django.contrib import admin
            from django.urls import path
            from project import views
            
            urlpatterns = [
                # path('admin/', admin.site.urls),
                path('', views.home),
                path('login/', views.login),
                path('auth/', views.auth, name='auth'),
                path('logout/', views.logout), # Added
            ]
        • project/views.py (created): This is practically the only file that I needed to modify later, in order to port the demo to use google-api-python-client and google-auth-oauthlib:
          import json
          from django.urls import reverse
          from django.shortcuts import render, redirect
          from authlib.integrations.django_client import OAuth
          
          CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
          oauth = OAuth()
          oauth.register(
              name='google',
              server_metadata_url=CONF_URL,
              client_kwargs={
                  'scope': 'openid email profile'
              }
          )
          
          def home(request):
              user = request.session.get('user')
              if user:
                  user = json.dumps(user)
              return render(request, 'home.html', context={'user': user})
          
          def login(request):
              redirect_uri = request.build_absolute_uri(reverse('auth'))
              return oauth.google.authorize_redirect(request, redirect_uri)
          
          def auth(request):
              token = oauth.google.authorize_access_token(request)
              user = oauth.google.parse_id_token(request, token)
              request.session['user'] = user
              return redirect('/')
          
          def logout(request):
              request.session.pop('user', None)
              return redirect('/')
        • project/templates/home.html (created):
          {% if user %}
          <pre>
          {{ user }}
          </pre>
          <hr>
          <a href="/logout/">logout</a>
          {% else %}
          <a href="/login/">login</a>
          {% endif %}
    • Google Apps Script API Python Quickstart code adaptation:
      • credentials.json contains the Client ID credentials (file downloadable from the GCP console) of the GCP project associated to the target Apps Script app
      • Flow class was used instead of InstalledAppFlow.
      • Instead of using pickles, OAuth2 token (a Credentials instance) is converted to a dictionary and saved to session (as a session key named token).
      • Value of session key user (which identifies the logged in user) is retrieved by parsing the Open ID Connect ID Token contained in the Credentials object resulting from a complete and successful OAuth2 flow.
  • Installing required libraries, creating the database and launching the dev server:
    cd app/backend-python-demo
    pip install Django google-api-python-client google-auth-oauthlib
    python manage.py migrate
    python manage.py runserver
    
  • Key online resources used:

2021-02-07

  • I have been extensively checking and testing some Python backend code (OAuth authentication, Apps Script API client...). I will share my findings in this repository as soon as I come with some significant results. So far I updated 2021-01-30 entry with a few details and analysis points.

2021-01-30 (Update: 2021-02-04)

  • Code refinements. Front HTML (index.html) will provide links to Filters and Logs spreadsheets when loading settings.
  • Three deployment options/scenarios to be considered:
    • Self-contained: Deployed as a web application, with both backend and frontend components served by the Apps Script project. The user should always have an active Google account session (already logged in to Gmail, for example) on the browser in order to access the webapp by its URL.
    • Webapp as a API gateway: Deployed as a web application, with all required sharing settings, and acting as a API gateway to all Google services being used. In other words, the deployed webapp will play the role of an app-level API, abstracting away features and technical requirements specific to each Google service (Gmail, Spreadsheets, Drive) and emulating a simple RESTful API using doGet() and doPost() methods; this API will then be "consumable" by any third-party app (serving as backend/frontend) as long as it supports OAuth2 authentication to Google services. In this case, we can just keep the default (Apps Script-managed) GCP project our Apps Script project would be automatically associated to. No need to switch to a standard (user-managed) one.
      • That said, we might actually still need a GCP project to identify and authenticate our Apps Script webapp users, i.e. users that have been previously granted the permission to execute by sharing the project with them. As far as I can tell, the Apps Script project doesn't have to be associated to that GCP project. In short, a bearer token (identifying the user) will be required in each POST or GET request sent to the webapp URL. This "bearer authentication" process is often taken care of by some OAuth library of the backend language/framework being used.
      • Setting the webapp to always execute as its owner while sharing it with multiple users, might be useful for centralizing configurations and logs; if the project is well designed to concurrently read from and write to Google spreadsheets, within Apps Script services daily quotas and limitations (that are subject to change at any time, without notice).
    • API-executable: Deployed as API-executable, after associating our Apps Script project to a standard (user-managed) GCP project, which would be an extra step that could be undesirable in some use cases. That said, the application will be manageable with just 3 functions: appinit(), getSettings() and setProperties(); all run (either directly or using a client library) through the well documented Apps Script API, using the script.run method.
      • Unlike webapp deployments, Apps Script projects deployed as "API-Executable" can only be executed as the user accessing them. This was confirmed after successfully executing this example code.

2021-01-27

  • Triggers of manually deleted Apps Script projects will keep running unless their files are removed from Drive's trash. Even then, these triggers would still show on https://script.google.com/home/triggers as anonymous/blank.


    triggers-of-deleted-projects.png

    That is, in fact, what caused the error I noted on 2020-05-25: Authorization is required to perform that action. So, to avoid this, we have to delete the triggers programmatically, or through "My Triggers - Apps Script" page.

  • Planning to refactor both project's structure and code, by splitting it into four components:

    • Core: Google Apps Script code (deployed as a webapp) as a managed "API gateway" to all required Google services (AppsScript, Gmail, Drive, Sheets).
    • Backend: as a middleware for authenticated access to a GMail-AutoResponder instance.
    • API: a API wrapper that will interact with the Core through authenticated HTTP requests.
    • Frontend: likely to be rendered on the backend (by template engine). Bootstrap 4 will be used.
  • TODO:

    • Add API documentation to README.
    • Logging to text files:
      • Use a common format for log entries: e.g. [W 2021-01-05 11:57:42.715 ModuleName] Log message
      • Log levels : App vs Infrastructure

2020-06-10

2020-05-28

  • I've decided to give the idea of "making an API proxy" another go, after checking @tanaikech's great write-up about GAS Web Apps. Here is the summary :


    AppsScript_doPost().png

    • The web app will be executed as the user accessing it, either the owner or any other Google user.
    • The Apps Script project needs to be shared with any Google user that we would want to access the web app.
    • The Google user accessing the web app is required to provide a OAuth access token as an authorization bearer in each GET or POST request sent to web app's URL.
    • On first time access, the user will be redirected to a web page to grant access to the scopes required by the app.
  • Communicationg through GET and POST requests to a Google Apps Script web app from a third party application, NodeJS for instance : A good starter code is provided in the Google documentation. There are a couple of adjustments and customizations to consider :

    • The example is geared towards CLI use, rather than in-browser use. So we'll need to modify the code to make it process web app OAuth credentials (with custom redirect URL...etc)
    • Manage authentication tokens using sessions/cookies instead of reading/writing server-side files.
    • Slightly redesign the authentication and the access workflow.
    • A sample code (both GAS backend and client NodeJS) will be separately developed as a PoC.

2020-05-25

2020-05-19 : Last commit of the month of Ramadan 🌙

  • Preparing code to test and progressively convert JavaScript client code to Ajax POST requests to be processed by the doPost() backend function.
  • Setting up a custom GCP project for a script gives you access to verbose and more informative logs on each function call, which turns out to be fairly useful when debugging.
  • Migrated raw_notes.md's section about "scripted deployment of Apps Script projects" to DevOps Lab repository.

2020-05-18

Since I often start working late at night, it is sometimes challenging to commit anything significant before midnight. So I'll leave this note here and be back a few hours later with more updates..

2020-05-17

  • For some reason, the format used for JavaScript files documentation header made functions unrecognized :
    TypeError: google.script.run.withSuccessHandler(...).withFailureHandler(...).appinit is not a function
    
    Corrected headers of the files appinit.js, Code.js and gmail-autoresponder.js.

2020-05-16

  • As I already pointed it out, I'm thinking about making the code base "convertible" to other backend language, and this is how I'm planning to proceed :
    • Client vs server JavaScript libraries : Separate functions that run on the backend from those mainly executed client-side.
    • Run Apps Script code as backend-only, and ultimately as a "API Proxy" : Deploy code as "API Executable"; All client-side tasks will be run through HTTP POST requests, i.e. doPost() will be the main entry point, instead of calling functions through the google.script.run API.

2020-05-15

  • Made the repository public.

2020-05-14

  • Bringing together all the needed Materialize components to rebuild the frontend page.


    Materialize

  • Exploring Materialize website, checking and testing examples and sample codes.


2020-05-13 : Missed 💢

...


2020-05-12

  • Exploring the possibility of building a fresh frontend with Materialize CSS framework, especially as there seem to be some good combinations with CKEditor Inline mode.
  • Been testing app's behaviour when the added switch is turned on or off: the app stops/starts sending responses accordingly, just as expected/desired.
  • I should consider adding emails from Google (like Apps Script 'Summary of failures' sent from apps-scripts-notifications@google.com) to From filters. I had some test instances respond to these emails.

2020-05-11

  • Added parameter (script user property) with binary value ('YES'/'NO') to enable/disable the app. Default value is 'NO'.
  • Refined a little bit README.md.
  • TODO.md content merged into raw_notes.md.

2020-05-10 (code)

  • First complete (backend and frontend) implementation of the cycle : Initialize WebAppModify settingsShow updated settingsReset WebApp:

    • New 3rd party component : SweetAlert2, used instead of JavaScript's alert().


    Init-Reset-Demo

  • I had some fun checking and comparing return values of the Apps Script method ScriptApp.getService().getUrl(), depending on multiple factors :

    • Account type (Free vs G-Suite),
    • Runtime (Legacy (Rhino, ES5) vs V8),
    • Caller of the function :
      • Manually, on the Apps Script Editor (Logger.log(ScriptApp.getService().getUrl()))
      • As a response to a GET request (doGet())
      • Client-side (google.script.run)
    • Version of the deployed code (latest/dev vs specific/prod)
  • Conclusion: the returned URL is almost unpredictable!

    1) Gmail.com (Free)

    • Script Editor : Logger.log(ScriptApp.getService().getUrl())
      • V8 Enabled : https://script.google.com/macros/s/{Dev-Deployment-ID}/dev
      • V8 Disabled : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec
    • Deployed Web App :
      • V8 Enabled :
        • GET response doGet() :
          • Dev version (latest code deployed) : https://script.google.com/macros/s/{Dev-Deployment-ID}/dev
          • Prod version (specific version deployed) : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec
        • Called client-side : google.script.run
          • Dev version (latest code deployed) : https://script.google.com/macros/s/{Dev-Deployment-ID}/dev
          • Prod version (specific version deployed) : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec
      • V8 Disabled :
        • GET response doGet() :
          • Dev version (latest code deployed) : https://script.google.com/macros/s/{Dev-Deployment-ID}/dev
          • Prod version (specific version deployed) : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec
        • Called client-side : google.script.run
          • Dev version (latest code deployed) : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec
          • Prod version (specific version deployed) : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec

    2) mydomain.com (G-Suite)

    • Script Editor : Logger.log(ScriptApp.getService().getUrl())
      • V8 Enabled : https://script.google.com/macros/s/{Dev-Deployment-ID}/dev
      • V8 Disabled : https://script.google.com/a/mydomain.com/macros/s/{Prod-Deployment-ID}/exec
    • Deployed Web App :
      • V8 Enabled :
        • GET response doGet() :
          • Dev version (latest code deployed) : https://script.google.com/macros/s/{Dev-Deployment-ID}/dev
          • Prod version (specific version deployed) : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec
        • Called client-side : google.script.run
          • Dev version (latest code deployed) : https://script.google.com/macros/s/{Dev-Deployment-ID}/dev
          • Prod version (specific version deployed) : https://script.google.com/a/mydomain.com/macros/s/{Prod-Deployment-ID}/exec
      • V8 Disabled :
        • GET response doGet() :
          • Dev version (latest code deployed) : https://script.google.com/a/mydomain.com/macros/s/{Dev-Deployment-ID}/dev
          • Prod version (specific version deployed) : https://script.google.com/macros/s/{Prod-Deployment-ID}/exec
        • Called client-side : google.script.run
          • Dev version (latest code deployed) : https://script.google.com/a/mydomain.com/macros/s/{Prod-Deployment-ID}/exec
          • Prod version (specific version deployed) : https://script.google.com/a/mydomain.com/macros/s/{Prod-Deployment-ID}/exec

2020-05-09

  • Intended behaviour of the webapp, depending on the flag value :
    • flag === value1 : Typically, first time run :
      1. Frontend : Apps settings form is disabled.
      2. Backend :
        • Any submitted data will be rejected (i.e. setSettings() won't modify any properties).
        • appinit() execution is allowed
    • flag === value2 : Web app already intialized/configured :
      1. Frontend : Apps settings form is enabled
      2. Backend :
        • Any form data submitted by the user will be applied.
        • appinit() execution is not allowed, unless an "App Reset" is explicitly requested.
  • I'm kind of resisting the idea of using templated HTML outputs, maybe because I don't want the code to be closely tied to Google Apps Script's specific concepts and "ways of doing things", and so keep it clean and "easily convertible" to other backend languages.
  • Exploring possible combinations, as far as allowed by the V8 runtime of the Google Apps Script framework⁽¹⁾⁽²⁾, of asynchronous execution and exception handling for both backend and frontend functions in order to address the above scenario.
  • Finished importing entries from the old worklog.
  • Some README.md refinements.

2020-05-08

  • Exploring : On page/webapp URL load, a backend flag value would be checked first. Depending on the value of this flag, the app would decide whether to load the form to view/update the settings (i.e. app already initialized), or call appinit() backend function (i.e. first time run).
  • Things to consider : As previously tested, loading dynamic HTML content into the frontend seems to require backend functions to return HTML code through HtmlService.createTemplateFromFile() (i.e. templated HTML) instead of HtmlService.createHtmlOutput().

2020-05-07

  • Relying on a GET parameter value, WebAppURL?reset=true for instance, is not safe because the URL is kept with it's parameters, so if the page gets reloaded the application will be reset again!

    function doGet(e) {
    
        var userProperties = PropertiesService.getUserProperties();
    
        if ( (userProperties.getProperty('INIT_ALREADY_RUN') !== 'YES') || (e.parameter.reset === 'true') ) {
    
            Logger.log(appinit());
            return HtmlService.createHtmlOutputFromFile('index')
                   .setTitle('Gmail AutoResponder - Settings');
        } else {
    
            return HtmlService.createHtmlOutputFromFile('index')
                   .setTitle('Gmail AutoResponder - Settings');
        }
    }
  • Odd behaviour of Google Apps Script for G-Suite accounts (first noticed on May 06, and thouroughly investigated on May 10):

    • When Chrome V8 runtime is enabled, ScriptApp.getService().getUrl() returns : https://script.google.com/macros/s/{Deployment-ID}/(exec|dev).
    • When Chrome V8 runtime is disabled, ScriptApp.getService().getUrl() returns : https://script.google.com/a/mydomain.com/macros/s/{deployment-id}/(exec|dev).
  • Tried Templated HTML with scriplets to "simulate" a page reload after resetApp() function is run : it is not possible to load a URL, dynamically provided by the scriplet <?= ScriptApp.getService().getUrl() ?> and using window.open(), into the active window, unless you override the recommended default behaviour of Google Apps Script, which protects against clickjacking by setting the X-Frame-Options HTTP header.


    X-Frame-Options

  • Using window.write(), overwrite index page with its same code returned, as a HTML templated code, by a backend function : page content seems to load without issues except CKEditor.


    window.write()

2020-05-06

  • Exploring ways to implement a "first time run" process to initialize the webapp, without having to go/redirect to a custom URL.
  • ScriptApp.getService().getUrl() doesn't seem to return the correct URL in case of a G-Suite account : https://script.google.com/a/mydomain.com/macros/s/AKfy-----------------------k9/dev : missing a/mydomain.com between https://script.google.com/ and /macros/s/AKfy-----------------------k9/dev.
  • Imported a few old Worklog entries.

2020-05-05

  • So I forgot that script properties accept only string values, and any other type would be converted, including boolean. Consequently, a if statement didn't work as expected since true and false were evaluated as literal non empty strings that are both equivalent to the boolean value true. Adjusted code accordingly.


    Properties_strings

  • Adjusted project's scopes : https://www.googleapis.com/auth/drive, instead of https://www.googleapis.com/auth/drive.readonly.

  • Now all app settings are configurable from the web frontend.

  • Deployed, intialized and run the application successfully with both G-Suite and free Google accounts.

  • Updated worklog.md and README.md.

2020-05-04

  • TIME_INTERVAL hardcoded to 10. because :
    • this value has proved to be reliable,
    • TIME_INTERVAL should be equal to the parameter initially given to the everyMinutes() method when script triggers were created, which there is no way to change afterwards other than deleting and recreating these triggers.
  • No need now for spreadsheet templates. Filters and Logs are generated and initialized by appinit().
  • Frontend and backend code for "getting" and "setting" NO_REPLY and STAR_PROCESSED_MESSAGE properties (more tests are needed).
  • Started enhancing the processing of properties values of setProperties() parameters object (set default values...etc).
  • User can either initialize or reset app settings using appinit().
  • Deleted non needed files from the repository.

2020-05-03 (code)

2020-05-02 (code)

  • How to tell if the application is run for the first time? The only way that comes to my mind is checking whether there is a user script property, 'alreadyRun' for example, of which the value ∉ {0, false, undefined, null, NaN, ""}.
  • Started appinit() function draft.

2020-05-01

  • App Init script roles : First time / run once script executed after the user has been authenticated (i.e. has granted access to his Google account) and the frontend URL has been visited (i.e. doGet() function is run) for the first time :
    1. Create Filters and Logs spreadsheets. Get URLs to show next to each one's input field.
    2. Place all app's files into the same Drive folder. Get and show URL of the folder.
    3. Create and set user script properties to their default values.
    4. [Create triggers [, set Enable/Disable App flag] ].
    5. Let the user manually enable the application.
    6. Load app parameters into the frontend and let the user modify and save them.
  • Time Zone :
    • I couldn't understand what reference time zone Apps Script uses when processing Date/Time data (Date objects, for instance). It was neither Script's nor Calendar's in my tests! It's not the time zone of the OS on which the browser is running, and not even that of the location I'm connecting from! It's just like Google would automagically guess your time zone! So why would I bother setting it in the first place?! And to make it more confusing, there can be different time zones under the same Google account, depending on the programmatical context: Calendar time zone, Script time zone, Spreadsheet's time zone...!

    • So I guess I will just settle with this solution, as stated by a StackOverflow user :

      “If there are multiple users of the script in different time zones, then I set the Time Zone in the script to +GMT 00:00 no daylight savings. And leave it at that.”🌎

    • And as a precautionary measure, I'll keep the DST offset parameter.

2020-04-30

  • I've been testing the options parameter of the method sendEmail(recipient, subject, body, options), which behaves the same as GmailThread.reply(body, options), with the following code :

    GmailApp.sendEmail('name@domain.com', 'Apps Script : Test message', 'This is a test messages from APps Script', {
        cc: null,
        bcc: null,
        noReply: null
    });
  • Tried a few combinations of values for cc, bcc and noReply properties, using both free and G-Suite Google accounts, and it seemed that it is safe to always default to null.

  • In addition, these methods check whether given email addresses (recipient, Cc and Bcc) are valid and throws the exception Invalid email if they're not. So there is no need to provide backend code to validate addresses.

  • Updated code accordingly.

  • I couldn't keep the same work pace in the last couple of days due to some preoccupations. So I'm just trying to keep my daily commitment..

2020-04-29

  • Telling whether it is a G-Suite or a Free account : I checked two online posts that I had added to raw_notes.md on March 28. One of them suggests a method that doesn't seem to work any longer. The other suggests a seemingly working solution relying on the Admin SDK Directory Service that needs to be enabled from the Script Editor UI through Resources->Advanced Google Services..., which I'm trying to avoid at the moment (unless it's required by other app features and/or makes things easier).

  • But, at the end, it seems that we can simply check whether script user's email address ends with gmail.com or some custom domain name! So I guess I'll settle with this for the moment :

    userProperties.setProperty('ISENABLED_NOREPLY', (Session.getActiveUser().getEmail().split('@')[1]!=='gmail.com')?true:false);

2020-04-28 (code)

  • Finished customizing CKEditor for form's text area :
    • Set a default response message body in the backend. Trying to set a placeholder text with CKEditor 4 on the frontend, and the Configuration Helper (confighelper) plugin seems needed. The configuration isn't straightforward, as almost all resources I could find online suppose that a custom configuration file config.js along with a directory containing needed plugins are used, i.e. available for any customization, which doesn't apply to our case.
    • Considering the desired customizations, I'm trying to figure out whether or not it's worth/easier to use CKEditor 5 instead.
    • Finally, I managed to create a basic example by checking the source code of this blog post by confighelper plugin author.

2020-04-27 (code)

  • I would prefer using CKEditor, as a plain JavaScript solution, over TinyMCE which is developed in TypeScript (and I have no plans to learn TypeScript at the moment).
  • According to most resources I checked, CKEditor creates its own DOM when replace() method is called, right at the end of page load, to replace form's textarea element. So, I had to force CKeditor to update the text area value using the updateElement() method before the form content is submitted to the backend. In addition, I had to use CKEditor's setData() method, instead of setting element's innerText property value, in order to update the text area with the content retrieved from the backend.
  • Customizing CKEditor toolbar..
  • Removed two directories from this repository :

2020-04-26 (code)

  • Tried a basic CKEditor 4 preset for message body text area, but the content is not sent to the backend on form submit. Falled back to plain HTML textarea which worked fine, for both retrieving and modifying settings. I'll try a basic TinyMCE setup and see.
  • Worked on another code repository.

2020-04-25 : First commit of the month of Ramadan 🌙

  • Since it is not possible to change script time zone from within a Script app, I'm trying to figure out a way for providing a "script user-side" time zone parameter that can be modified using the frontend, and leave script's time zone at "GMT+00".

  • A Google user isn't given a default profile picture if he has never set one manually. So, the app should provide an alternate/default picture in case getPhotoUrl() returns null.


    No Profile Picture

  • Added a default user profile picture encoded in base64

  • It would make sense to retrieve app's settings on page load if a "enable/disable app" switch is used. There seem to be two choices for an app status switch :

    • A boolean script user property to be set accordingly.
    • Function(s) to create and delete triggers.
  • raw_notes.md revision :

    • Deleted old non-needed entries.
    • Moved some "TODO" entries to TODO.md.
  • Custom errors to be created for / thrown by setProperties() function :

    • Provided Spreadsheets IDs are not valid, either because the resources do not exist or are not readable/writable by the script user.
    • Invalid start/finish hours
    • Invalid execution time interval
    • Invalid Cc email address.
    • "Reply with 'noreply' address" is set for non G-Suite user.
    • Message body exceeds maximum number of characters. Format/Content not allowed.

2020-04-24

  • Enhanced code across the repository :
    • Code.js : get all parameters from script user properties, otherwise assign default values.
    • Replaced LOG_SS_ID with LOGS_SS_ID.
    • frontend_index.html :
      • Added link to revoke script's access to user data (logout).
      • Added default values next to input fields
  • Refined/Updated README.md and worklog.md.

2020-04-23 (code)

  • Basic frontend example can now "set" Apps' settings.

  • A couple of backend functions now use objects as parameters, instead of arrays. Consequently, there was one less function needed which was removed.

  • Added some basic styling to highlight data retrieved from backend app.


    Basic Get/Set

  • All frontend functions moved to gmail-autoresponder.js.

2020-04-22

  • Got a basic file upload example to work properly. (Code)


    Successful Upload to Drive

  • First working frontend example that retrieves App settings from backend. Code : frontend_index.html, frontend.js


    Demo Load From Backend

  • Refined a little bit worklog.md.

2020-04-21 (code)

  • For some reason, files created on Drive from Blob data (= input file of a submitted form) lose their MIME type and get corrupted. What I couldn't understand is that up until the file is uploaded to the server, and right before a Drive file is created with its data by calling DriveApp.createFile(blob), the blob type is correct. The backend function processForm() of draft_code\client-to-server\Code.js was modified to illustrate the issue :


    Corrupted Drive Files

  • So basically, some fairly reliable resources and accepted solutions on the web suggest to first process the submitted file with FileReader(), and pass it as a data URL to a backend function for a second process that extracts content type from it, decodes the submitted base64 data, and calls Utilities.newBlob() to create a new blob object for DriveApp.createFile(blob).

  • Here is a basic draft code as a wrap-up of what I've understood so far from the examples I studied. For the time being, this focuses only on that content type issue. Further development is needed to process forms with multiple types of input data (not only file upload) :

    • Frontend (client) :

      /* // Example 1
      function sendFileToDrive(file) {
          var reader = new FileReader();
          reader.onload = function (event) {
              var content = reader.result;
              google.script.run.withSuccessHandler(updateProgressbar)
                               .uploadFileToDrive(content, file.name);
          }
          reader.readAsDataURL(file);
      }
      */
      
      // Example 2
      function sendFileToDrive(file) {
          var reader = new FileReader();
          reader.onloadend = function (event) {
              google.script.run.withSuccessHandler(updateProgressbar)
                               .uploadFileToDrive(event.target.result, file.name);
          }
          reader.readAsDataURL(file);
      }
      
      // On form submit
      function FileUpload() {
          var allFiles = document.getElementById('myFile').files;
          sendFileToDrive(allFiles[0]); // Since there is only 1 file
      }
    • Backend (Google Apps Script):

      /* // Example 1
      function uploadFileToDrive(base64Data, fileName) {
      
          var contentType = base64Data.substring(5, base64Data.indexOf(';'));
          var bytes = Utilities.base64Decode(base64Data.substr(base64Data.indexOf('base64,') + 7));
          var blob = Utilities.newBlob(bytes, contentType, fileName);
          var driveFile = DriveApp.createFile(blob);
      }
      */
      
      // Example 2
      function uploadFileToDrive(base64Data, fileName) {
      
          var splitBase = base64Data.split(',');
          var contentType = splitBase[0].split(';')[0].replace('data:', '');
          var bytes = Utilities.base64Decode(splitBase[1]);
          var blob = Utilities.newBlob(bytes, contentType);
          blob.setName(fileName);
          var driveFile = DriveApp.createFile(blob);
      }

2020-04-20 (code)

  • Objective : Provide a same web page / frontend to both get current app's parameters' values (= prefill form fields on page load using google.script.run to call server-side "getters") and update them on submit.

  • Researching and testing code about Google Apps Script Client-to-Server Communication


    C2S_Demo

  • There are still concepts that I'm trying to deeply understand how they imply or impact each other, namely script scopes, APIs' scopes, whether or not it is required to connect to a GCP project, deploying as "a web app" vs "API Executable"... For instance, I had to publish the app as "API Executable" to be able to run through the Apps Script API some initialization functions (providing 'Logs' and 'Filters' spreadsheets' IDs...etc). But now, as I'm working on a frontend, I have to publish the app as "a web app" to issue client-to-server calls and provide a convenient way to show and update app's configs. So I guess, I will just make my best to both learn and enhance my code as I go.


2020-04-19 : Missed 😔

But hey, I normally “code” more than 1 hour a day! C'mon, it's not that bad!


2020-04-18

  • Working on a basic HTML file that will let us "get" and/or "set" app's parameters.
    • Information to retrieve/modify :
      • User's profile picture
      • User's name email
      • Time zone
      • Message body (HTML)
      • Script user's parameters :
        • FILTERS_SS_ID
        • LOGS_SS_ID
        • START_HOUR
        • FINISH_HOUR
        • TIME_INTERVAL
        • DST_OFFSET
    • I had to add another scope https://www.googleapis.com/auth/drive.readonly to retrieve the URL of script user's photo

2020-04-17

Significant update of README.md's structure and content (draft). Started adding old worklog entries translated from French.

2020-04-15, 04-16

Still refining README.md, along with some modifications here and there.

2020-04-14

  • Cleaning up README.md

2020-04-13 (Update: 2021-04-06)

2020-04-03 - 04-12 (intermittently)

  • I had to use Fiddler to intercept sequences of HTTP requests to Drive API URLs while using gdrive . Trying to mimic these requests using CURL.
  • After days of researching, I finally managed to find CURL command lines corresponding to each of the following Drive operations :
    1. [Not Curl] Construct Access Token request URL; Get the code to use in the next step
    2. Request Access Token / Authorization Code
    3. Request a new Authorization Code using the refresh token
    4. Upload files to Drive; Import XLSX file as a Google SpreadSheet
    5. Create Drive Directory
    6. Move a Drive file to another folder
    7. Other useful operations :
      • Getting information about the authenticated Google user
      • Rename a file in Drive
  • CURL will be used instead of gdrive from now on.
  • I will probably document what I have found about dealing with Google Drive API using curl in dedicated MD file/Repo/Gist/Blog post.
  • So, there is no need now to use Google Apps Script code in order to upload/import files to Drive. Gotta update code accordingly.

2020-04-03, 04-04

  • Exploring alternative ways :
    • To make [continuous] deployment of the application as automated as possible.
    • To make the initial setup of the application easy, with one function call.
    • To upload/import files to Drive using just the API, without relying on third-party tools like gdrive.
  • Restructuring/Cleaning code.

2020-04-01, 04-02

Drafting instructions for project setup using both clasp and gdrive. Deployed successfully an updated version of the code.

2020-03-29, 03-31 (code)

Exploring clasp tool for automated deployment of Google Apps Script project. Successfully deployed a first version of the code.
Associate_AppsScript_to_GCP

2020-03-28 : First commit during COVID-19 national lockdown 😷 (code)

Documenting: Collecting notes about app logic, features and auto-deployment

2019-10-31, 11-19

Added new examples of AppScript execution errors (from summary).

2019-08-26

Added first draft of README.md.

2018-09-21, 11-20 (code)

Added and updated sample frontend code using Material Design Lite.


Entries to be translated from the old worklog :

2018-09-10

Original :

Revue du code source de l’application web, après plus d’un an d’exécution continue en production, avec plus de 6700 réponses automatiques envoyées.

Liste exhaustive des types d'erreurs reportées par Google Apps Scripts (résumés en provenance de l'adresse apps-scripts-notifications@google.com) durant l'année, illustré chacun par un exemple. Informations à prendre en considération dans les prochaines améliorations du code:

Start Function Error Message Trigger End
10/07/2018 20:06 autoReply Limit Exceeded: Email Body Size. (line 99, file "Code") time-based 10/07/2018 20:06
10/04/2018 20:43 autoReply Document {DOCUMENT-ID-DELETED} is missing (perhaps it was deleted, or you don't have read access?) (line 22, file "Code") time-based 10/04/2018 20:44
9/17/18 12:53 AM autoReply Service error: Spreadsheets (line 63, file "Code") time-based 9/17/18 12:53 AM
7/26/18 11:16 PM autoReply Gmail operation not allowed. (line 62, file "Code") time-based 7/26/18 11:16 PM
4/24/18 4:52 AM autoReply Service timed out: Spreadsheets (line 63, file "Code") time-based 4/24/18 4:53 AM
3/23/18 10:46 PM autoReply We're sorry, a server error occurred. Please wait a bit and try again. time-based 3/23/18 10:46 PM
1/25/18 8:22 PM autoReply We're sorry, a server error occurred. Please wait a bit and try again. (line 125, file "Code") time-based 1/25/18 8:24 PM
12/01/2017 08:45 archiveLog Sorry, it is not possible to delete all non-frozen rows. (line 26, file "Archive_Log") time-based 12/01/2017 08:45
10/02/2017 20:58 autoReply Argument too large: subject (line 97, file "Code") time-based 10/02/2017 20:58
10/01/2017 08:02 archiveLog You do not have permissions to access the requested document. (line 11, file "Archive_Log") time-based 10/01/2017 08:02
8/30/17 11:06 PM autoReply Invalid email: Judith Pin <> (line 92, file "Code") time-based 8/30/17 11:06 PM

2017-12-11 (code)

Original :

Modification des codes source afin de rectifier un problème empêchant la réinitialisation mensuelle de la feuille du journal des sessions d’exécutions.

2017-11-14

Original :

En vérifiant les journaux des messages traités ainsi que les occurrences d’exécution de la session du 13/11/2017 : la session s’est déroulée correctement après les dernières mises à jours des codes source.

2017-11-13 (code)

Original :

Les sessions d’exécution du 11/11/2017 et le 12/11/2017 du programme de réponses mail automatiques du compte OPERATIONS se sont correctement déroulées après la dernière mise à jour du code source. Les changements ont été généralisés sur les autres programmes des comptes OPERATIONS2, OPERATIONS3, OPERATIONS4, OPERATIONS5 et OPERATIONS6. Par ailleurs, des fonctions pour effacer mensuellement le journal des occurrences de chaque session d’exécution des programmes ont été ajoutées à leurs codes source respectifs.

2017-11-11

Original :

Fin de la nouvelle version du code source. Premier déploiement pour le compte OPERATIONS. Le code sera au fur et à mesure amélioré selon les résultats.

2017-11-10

Original :

Continuation du développement du code amélioré du programme des réponses mail automatiques.

2017-11-09

Original :

Continuation du développement et test des premières améliorations du code source pour une meilleure performance d’exécution.

2017-11-08

Original :

Début d’optimisation du code source pour une meilleure performance d’exécution : Mise à jour du code envisagés : Au lieu d’extraire, à chaque exécution, tous les identifiants des messages traités durant tout le mois depuis le journal des opérations pour vérifier si un message n’a pas été déjà traité, le programme vérifierait juste les identifiants des messages traités dans la dernière occurrence qui seraient déjà mis en cache.

2017-11-01

Original :

Vérification des programmes (journaux et configurations) :

  • L’archivage des journaux du mois d’octobre a été correctement exécuté pour toutes les instances du programme.
  • Adresses ajoutées à la liste d’exclusion From de chacun des documents de configuration.

2017-10-30

Original :

Les codes sources des programmes de réponses mail automatiques ont été mis à jour suite au changement de l’heure locale qui a eu lieu le 29/10/2017.

2017-10-23

Original :

Les deux premières sessions d’exécution des programmes de réponses mail automatiques associés aux comptes OPERATIONS5 et OPERATIONS6 ont été respectivement exécuté le 21/10/2017 et le 22/10/2017. 4 réponses automatiques ont été envoyées, 9 messages reçus sautés. Les adresses expéditrices avec la mention do-not-reply ont été ajoutées à la liste d’exclusion. Les résultats des sessions OPERATIONS5 et OPERATIONS6 seront suivis durant toute la semaine afin de corriger toute éventuelle anomalie. N.B. : Depuis l’exécution de la première session de réponse mail automatique le 23/08/2017, 1203 réponses ont été envoyées.

2017-10-21

Original :

Configurations des programmes de réponses mail automatiques pour les comptes OPERATIONS5 <operations5@mycompany.com> et OPERATIONS6 <operations6@mycompany.com>. Les premières sessions seront exécutées le jour même à partir de 20:00 (heure locale).

2017-10-05

Original :

Etudes, rectification et suggestion d’amélioration suite aux remarques formulées dans le rapport du 03/10/2017:

  • Rectification du document de configuration du programme de OPERATIONS2 auquel une opération d’archivage a été appliquée par erreur ; ce qui causait le traitement de l’intégralité des messages reçu sans aucun filtrage.

  • Une amélioration du code est à envisager suite aux erreurs reportées par le service Google Apps Script :

    2017-10-05 - Gmail-AutoResponder

    • Les messages d’erreur Argument too large: subject (line 97, file "Code") et Limit Exceeded: Email Body Size. (line 97, file "Code") indiquent que le corps du message de réponse composé du texte informatif principal et de l’historique de la conversation peut potentiellement dépasser la limite de la taille maximale du corps de message de réponse.
    • Le concept permettant de contourner ce problème peut être résumé comme suit :
      • L’ensemble du message (texte informatif + historique de la conversation) sera initialement stocké dans une chaîne de caractère (String).
      • Si la taille de la chaîne dépasse 20Ko l’excédent sera supprimé et remplacé par des points de suspension.

2017-10-03

Original :

Retour sur les résultats des sessions d’exécution des programmes associés aux comptes OPERATIONS, OPERATIONS2, OPERATIONS3 et OPERATIONS4 entre le 22/09/2017 et 02/10/2017.

  • Premières remarques :
    • Aucun filtrage n’a été appliqué aux messages reçus durant la session du 01/10/2017 et le 02/10/2017 du programme de OPERATIONS2. Une réponse automatique a été envoyée pour chaque message détecté et traité.
    • La nécessité d’ajouter une colonne au journal contenant l’éventuelle raison d’exclusion d’un message se confirme.
    • La couleur de remplissage des lignes, distinguant les messages sautés des réponses automatiques envoyée, n’a pas été correctement appliquée entre le 24/09/2017 et le 30/09/2017 aux journaux de OPERATIONS et OPERATIONS2. Il ne peut s’agir que d’un bug/disfonctionnement du programme.

2017-09-23

Original :

Configuration du programme de réponses mail automatique pour un quatrième compte Google: OPERATIONS4 <operations4@mycompany.com>.

2017-09-22

Original :

Evaluation des résultats des sessions d’exécution du 20/09/2017 et 21/09/2017: 23 réponses envoyées.

2017-09-20 (code)

Original :

  • Evaluation des résultats des sessions d’exécution du 19/09/2017: 26 réponses envoyées :
  • Mise à jour du code: Extension de deux minutes de l’intervalle de recherche des derniers emails reçus sur chacune des trois boîtes emails à chaque itération du programme afin de ne pas rater les emails coïncidant avec l’instant d’exécution.

2017-09-19 (code)

Original :

  • Evaluation des résultats des sessions d’exécution du 18/09/2017: 19 réponses automatiques envoyées :
    • OPERATIONS : 60 itérations du programme ayant récupérés 68 threads. 68 messages traités, dont 50 sautés et 18 réponses automatiques envoyées.
    • OPERATIONS2 :
      • 60 itérations du programme ayant récupérés 38 threads. 38 messages traités et sautés.
      • 1 message non traité:
        • L’heure de réception a coïncidé avec le déclenchement de la deuxième itération du programme pendant la session d’exécution du 18/09/2017. La partie du code recherchant et récupérant les derniers mails reçus l’aurait, par conséquent, raté.
        • Le script du compte OPERATIONS3 a détecté et sauté le message, comme il est configuré pour exclure les messages à destination de OPERATIONS et OPERATIONS2 (adresses respectives ajoutées à la colonne TO_BLACKLIST du document de configuration Autorespond-config de l'instance de l'application associée au compte Google OPERATIONS3).
        • Le script du compte OPERATIONS n’a pas détecté le message vu que l’itération qu’il l’aurait traité (exécutée à 19:26:02 GMT) a détecté un message plus récent dans la même conversation et auquel une réponse a été en effet envoyée.
        • De toute façon, le message a été traité peu après par l’équipe OPERATIONS2 même.
    • OPERATIONS3 : 60 itérations du programme ayant récupérés 25 threads. 25 messages traités, dont 24 sautés et une réponse envoyée.
  • Ajout du fichier Autorespond-config-OPS3.xlsx au code source:
    • Un deuxième modèle du fichier Autorespond-config a été ajouté au code source, illustrant -à titre d'exemple- la configuration utilisée pour l'application associée au compte OPERATIONS3, l'empêchant d'envoyer une réponse à un message reçu si celui-ci est aussi destiné au moins à l'une des adresses operations@mycompany.com et operations2@mycompany.com et serait donc traité par l'une des applications respectives leur étant associées.
    • A cet égard, les 3 instances en exécution sont en effet configurées comme suit:
      • OPERATIONS : Traite tous les messages répondant aux critères de filtrage préconfigurés, excluant ainsi:
        • les accusés de lectures
        • les messages d'administration système (postmaster, mailer-daemon)
        • les messages en provenance des adresses mail de la société (*@mycompany.*)
        • les messages en provenance des adresses avec l'alias noreply/no-reply.
        • les messages en provenance des adresses ajoutées au fur et à mesure à la liste d'exclusion FROM_REGEX_BLACKLIST
        • les messages à destinations anonymes undisclosed-recipients.
      • OPERATIONS2 : en plus des critères de filtrage précités, elle ne traite pas les messages destinés aussi à operations@mycompany.com.
      • OPERATIONS3 : en plus des critères de filtrage précités, elle ne traite pas les messages destinés aussi au moins à l'une des adresses operations@mycompany.com et operations2@mycompany.com.
    • Une même approche sera adoptée pour les autres instances de l'application qui seraient ultérieurement ajoutées et associées à d'autres compte Google.

2017-09-18 (code)

Original :

Evaluation des résultats des sessions d’exécution du 16/09/2017 et 17/09/2017 : 31 réponses automatiques envoyées :

  • OPERATIONS :
    • 120 itérations du programme ayant récupérés 84 threads. 84 messages traités, dont 63 sautées et 21 réponses automatiques envoyées.
  • OPERATIONS2 :
    • 120 itérations du programme ayant récupérés 53 threads. 53 messages traités, dont 51 sautées et 2 réponses automatiques envoyées.
  • OPERATIONS3 :
    • 120 itérations du programme ayant récupérés 95 threads. 95 messages traités, dont 88 sautées et 7 réponses automatiques envoyées.
  • Améliorations et mises à jour :
    • Les codes source ont été mis à jour pour activer le suivi (= ajouter une étoile au message sur le client webmail Gmail) de chaque message traité.

2017-09-16 (code)

Original :

Evaluation des résultats des sessions d’exécution du 14/09/2017 et 15/09/2017.

  • Améliorations et mises à jour :
    • Ajout d'une nouvelle adresse aux listes d’exclusions respectives From : de chaque compte (i.e. OPERATIONS, OPERATIONS2, OPERATIONS3).
    • Modification du code pour la mise en copie normale Cc (au lieu de Cci) des adresses d’administration pour un meilleur filtrage et suivi des réponses automatiques envoyées (i.e. pour une meilleure lisibilité sur l’application web Gmail avec des libellés personnalisés, par exemple). L'adresse mise en copie est en effet un alias de amine@mycompany.com; Dans le cas des réponses automatiques programmées jusqu’au 16/09/2017, les alias sont respectivement : it-operations@mycompany.com, it-operations2@mycompany.com, it-operations3@mycompany.com.
    • OPERATIONS2 et OPERATIONS3 : A partir de la session d’exécution du 16/09/2017, le système de filtrage des messages reçus par destination vérifiera les champs Cc : et Cci : en plus du champ To :.
    • Prévisions : Comme un identifiant unique est attribué à chaque version d’un même message envoyé à plusieurs destinataires de *@mycompany.*, il va falloir penser à un autre critère de filtrage de tels messages pour qu’ils ne soient pas traités plusieurs fois. L’identifiant Message-ID, selon les spécifications du document « RFC 822 » de l’IETF, répond le plus aux critères requis. Une expression régulière pour l’extraction de cet identifiant a été développée et préparée pour utilisation dans de prochaines versions du programme : ^Message-ID:\s*[<A-Za-z0-9!#$%&'*+-/=?^_`{}|~.@]*
      • Révision (2019-06-09): Il est possible d'extraire l'en-tête Message-ID sans avoir à utiliser une expression régulière sur tout le contenu du message original, et ce en utilisant la méthode getHeader(name) de la classe GmailMessage.

2017-09-14

Original :

Evaluation des résultats de la session d’exécution du 13/09/2017 : 60 itérations correctes du programme ayant récupérés 33 threads. 33 messages traités, dont 19 sautées et 14 réponses automatiques envoyées. Déploiement de versions adaptées du programme pour les deux comptes OPERATIONS2 et OPERATIONS3.

2017-09-13

Original :

Evaluation des résultats de la session d’exécution du 12/09/2017 : 60 itérations correctes du programme ayant récupérés 53 threads. 53 messages traités, dont 42 sautées et 11 réponses automatiques envoyées.

2017-09-12

Original :

Evaluation des résultats de la session d’exécution du 11/09/2017 : 60 itérations correctes du programme ayant récupérés 66 threads. 66 messages traités, dont 45 sautées et 21 réponses automatiques envoyées. Un message récupéré depuis la boîte email operations@OldMailServer.com n’a pas été traité. L’hypothèse établie dans le rapport du 09/09/2017 se tient.

  • Révision (2019-06-08) : Le script de test initial aurait été créé pour vérifier si l'application avait déjà traité des messages envoyés au compte non-Google operations@OldMailServer.com et récupérés sur la boîte Gmail G-Suite operations@mycompany.com, et ce en vérifiant les IDs des threads (auxquels ces messages sont respectivement attribués par Gmail), retournés par la requête de recherche, contre ceux des messages/threads traités journalisés dans le document Google Spreadsheet Autorespond-log. Néanmoins, l'analyse du 12/09/2017 porte ici à confusion, et comme les résulats du 11/09/2017 ne sont plus vérifiables, un nouveau script de test (voir code) a été développé afin de confirmer que ces threads sont traités d'une façon normale, c.à.d. que l'ID Gmail (qui est différent du Message-ID de la norme RFC 822) du thread correspond à l'ID de son premier message. A noter que ces messages risquent quand même de ne pas être relevés par l'application à cause des éventuels retards mis à leur récupération.

2017-09-11

Original :

Evaluation des résultats des sessions d’exécution du 09/09/2017 et 10/09/2017 : 73 messages traités, dont 51 sautées et 22 réponses automatiques envoyées.

2017-09-09

Original :

Analyse des résultats de la session d’exécution du 08/09/2017 :

  • 58 messages traités, dont 44 sautées pour des raisons valides et 14 réponses envoyées.
  • 2 messages non traités :
    • MESSAGE (1): L’heure de réception du message était très proche (en amont) de l’instant de l’itération du programme qui a eu lieu exactement à 19:06 (GMT).
    • MESSAGE (2): Le message a été reçu sur la boîte email operations@OldMailServer.com et récupéré sur la boîte principale operations@mycompany.com.
      • Rédaction en cours d'un script de test (voir code) afin de vérifier si de tels messages auraient compromis un traitement pertinent des messages: Récupération des IDs des threads auxquels appartiennent les messages envoyés [exclusivement] à l’adresse operations@OldMailServer.com. Vérifier les valeurs récupérées contre les journaux des messages traités.

2017-09-08

Original :

Evaluation des résultats de la session d’exécution du 07/09/2017 : 36 messages traités, dont 27 sautées pour des raisons valides et 9 réponses envoyées.

2017-09-07 (code)

Original :

Analyse des résultats de la session d’exécution du 06/09/2017 :

  • Le projet Google Apps Script AutoRespond est configuré avec un déclencheur en fonctions du temps qui se lance après chaque 10 minute. Les heures des premières et dernières exécutions effectives du programme sont respectivement 19:06 (GMT) et 04:56 (GMT). En effet, le programme s’exécute continument après chaque 10 minute durant toutes les 24 heures. Cependant le traitement des messages reçus n’est effectué que si la condition est satisfaite, à savoir : être dans la plage horaire [20h-06h].
  • Comme les messages Gmail sont organisés sous forme de threads (conversations), le programme récupère à chaque exécution les nouveaux threads ainsi que ceux mis à jour (c.à.d. réception de nouvelles réponses à une ancienne conversation (échange)) dans les dernières 10 minutes et traite ensuite leurs derniers messages respectifs.
  • Pour chaque thread, si le dernier message ne répond à aucun critère d’exclusion, une réponse automatique lui serait envoyée ; sinon, le programme passe au thread suivant. Dans chacun des deux cas, les messages traités ainsi que le nombre de threads récupérés à chaque exécution sont journalisés. Les informations du journal (log) permettent le suivi, l’analyse et le diagnostic des éventuels problèmes rencontrés.
  • Deux cas de figures se sont présentés suite à la dernière session d’exécution; qui peuvent nécessiter une analyse de comportement du programme, une évaluation des risques ainsi que des éventuelles améliorations du code source :
    • MESSAGE (1): Le message a été reçu vers 21:16 mais n’a pas été traité.
      • Vu qu’il était le dernier message de son thread jusqu’à la fin de la session d’exécution, la seule cause apparente serait le fait que sa réception a coïncidé avec le déclenchement du programme et il n’a pas été récupéré parmi les messages reçus dans les dernières 10 minutes.
      • Le message n’a pas été non plus récupéré dans l’itération suivante.
      • Il serait donc plus prudent d’ajouter une marge d’erreur à l’intervalle de temps. Ce serait pratique d’attribuer une valeur dynamique à cet intervalle, soit par exemple 1.5x la durée séparant deux exécutions (automatique) consécutives du programme. Documentation en cours sur les moyens disponibles pour y parvenir.
    • MESSAGE (2): Deux minutes après, et dans le même thread, le message a été suivi par un autre (i.e. une réponse) envoyé par (accounting@mycompany.com) avec l’adresse (operations@mycompany.com) en copie.
      • Vu que le dernier message du thread venait d’une adresse mail exclue, il a été sauté.
      • Le présent cas laisse à penser à d’autres cas probables pouvant être critique, comme celui d’un message répondant à tous les critères de réponse automatique suivi juste après, dans le même thread, d’un mail exclu mais sans qu’il soit envoyé depuis une autre adresse email de MyCompany; cas d’un accusé de lecture d’un ancien message dans le même thread reçu du même expéditeur, par exemple.
      • Etude en cours d’améliorations et méthodes alternatives pour traiter avec plus de prudence les files des derniers messages reçus.

2017-09-06

Original :

Analyse des résultats de la session d’exécution du 05/09/2017 :

  • 57 messages traités : 18 réponses envoyées, 39 messages sautés pour des raisons valides.
  • Les réponses automatiques envoyées ont couverts tous les threads Gmail reçus dans la plage horaire [20h-06h].
  • Mises à jour mineures du code source.
  • Documentation sur les améliorations envisagées :
    • Utilisation du Cache : pour le stockage temporaire et la consultation rapide des identifiants des derniers messages traités, au lieu d’extraire et de rechercher dans toutes les valeurs de la colonne D Message ID de la première feuille du document Google Spreadsheet de journalisation Autorespond-log.
    • Etude d’une nouvelle architecture Master/Slave du programme pour déclencher simultanément les réponses automatiques de plusieurs comptes depuis un même script asynchrone avec des requêtes HTTP POST.

2017-09-05

Original :

Évaluation de l’exécution du programme du compte OPERATIONS entre le 31/08/2017 (soir) et le 05/09/2017 (matinée):

  • 162 threads détectés, dont 114 sautés et 48 réponses envoyées.
  • Ajout d'adresse mails à la liste d’exclusion.

2017-08-31

Original :

Evaluation de l’exécution de la session du 31/07/2017 :

  • 50 mails traités, dont 37 sautés.
  • Ajoutée des adresses à la liste d’exclusion
  • Un message SPAM sans une vraie adresse envelop sender ni From : a déclenché une erreur, puisque la méthode GmailMessage.getFrom() dans le code a retourné la valeur Judith Pin <> qui n’est pas une adresse valide pour envoyer une réponse avec la méthode GmailThread.reply(body, options). Bien que l’erreur n’était pas bloquante et ait été bien reportée par email, il serait plus judicieux de journaliser de tels cas avec le message d’erreur comme note. Cette suggestion peut faire objet d’une future amélioration du code avec des éventuels traitements d’exceptions.
  • Idées et prévisions : Exécution du programme sur toutes les boîtes emails depuis un même programme central :
    • La méthode la plus sûre serait de communiquer, depuis le script operations, avec d’autres scripts Google Apps Script associés aux autres comptes, en utilisant des requêtes HTTP POST sur des connexions chiffrées (SSL/TLS).
    • Comme prérequis, il va falloir d’abord publier chacun des autres scripts en tant qu’application web (avec d’éventuelles restrictions d’accès pour protéger les données).
    • Les autres scripts auraient les droits suivant :
      • Modification du document Google Spreadsheet : Autorespond-log, propriété du compte operations@mycompany.com.
      • Lecture depuis le document Google Spreadsheet : Autorespond-config, propriété du compte operations@mycompany.com.
    • Consultation en cours des documentations ainsi que les forums d’aide et support officiels aux sujets précités.

2017-08-30 (code)

Original :

Confirmation de l’exécution optimale de la session du 29/07/2017 du programme associé au compte Google operations@mycompany.com. Le test d’archivage du journal des messages traités s’est exécuté comme planifié et avec succès. Dorénavant, au début de chaque mois, l’historique des opérations du mois précédent sera archivé dans une feuille séparée du même fichier. Etude en cours de la possibilité de gérer les réponses automatiques de toutes les boîtes emails de la société avec un seul programme lisant les configurations depuis un même fichier et enregistrant toutes les opérations exécutées dans le même journal. Si cela s’avère faisable, une révision considérable du tout le code source serait nécessaire.

2017-08-29

Original :

Confirmation de l’exécution optimale de la session du 28/07/2017 du programme associé au compte Google operations@mycompany.com avec les dernières mises à jours du code.

  • Améliorations :
    • Inclusion du code HTML constituant le corps du message de réponse dans un fichier HTML du même projet Google Apps Script, au lieu de l’importer depuis un fichier externe hébergé sur un espace d’hébergement web tiers.
    • Rajout d’un fichier de script supplémentaire au même projet et sa programmation pour exécution automatique mensuelle. Le script archivera les opérations (i.e. réponses envoyées, et messages sautés) de chaque mois dans une nouvelle feuille du document log Spreadsheet Autorespond-log afin d’alléger la feuille principale du journal.
    • Le code source sera publié le 30/08/2017
  • Configuration provisoire pour test et validation : le script Archive_log.gs a été paramétré pour exécution automatique mensuelle le 30 de chaque mois ; en l’occurrence, une première exécution aurait lieu le lendemain 30/08/2017.

2017-08-28

Original :

Evaluation des performances du programme associé au compte operations@mycompany.com durant son exécution du 26/08/2017 au 28/08/2017 :

  • 87 messages traités:
    • 16 réponses automatiques envoyées, dont 5 étaient non pertinentes (adresses email à ajouter à la liste d'exclusion)
    • 71 messages sautés pour des raisons valides.
  • Améliorations du code :
    • Ajout d’une nouvelle feuille dans le fichier de journalisation Autorespond-log pour l’enregistrement de l’heure d’exécution ainsi que le nombre de conversations (threads) Gmail (récupérés et traités) du dernier intervalle de temps (prédéfini, et après lequel le programme se ré-exécute).
    • Marquage de la fin de session d’exécution globale (20h-06h) sur chacune des feuilles du journal afin d’en faciliter la lecture et l’analyse.
    • Le code source sera publié le 30/08/2017

2017-08-26

Original :

Evaluation des performances du programme associé au compte operations@mycompany.com durant son exécution entre 25/08/2017 à 20:00 et 26/08/2017 à 06:00 :

  • 32/33 des messages reçus dans la plage horaire 20h-06h ont été traités.
    • 1 message non détecté. Il s’agit d’un deuxième accusé de lecture du même message par le même destinataire. Vu que les deux emails font partie de la même conversation (thread) Gmail, seul le plus récent a été traité.
    • 3 réponses automatiques envoyées
    • 29 messages exclus pour des raisons valides
  • Vu le résultat assez satisfaisant de son exécution, le code source -en sa dernière version- sera retenu.

2017-08-25 (code)

Original :

Bilan de l’exécution du programme pour le compte operations@mycompany.com pour le 24/08/2017 :

  • 17 réponses automatiques envoyées entre 20:28 et 06:35 (heure locale)
  • Amélioration du programme :
    • Réorganisation des lignes de déclaration des variables pour une meilleure lisibilité et portabilité du code.
    • Enregistrement des configurations sur une seule feuille du document Autorespond-config avec plusieurs colonnes, au lieu de plusieurs feuilles contenant chacune un filtre. L'ancienne version fichier a été archivée sous le nom Autorespond-config_OLD-till-2017-08-24. Adaptation du code.
  • Rajout d’une valeur de décalage pour faciliter l’ajustement de la plage horaire d’exécution en cas de changement de l’heure locale.
  • Utilisation d’une adresse générique no-reply afin de dissuader les destinataires de répondre directement aux messages automatiques. Par ailleurs, cela nous épargnera de configurer et maintenir sur chaque installation du logiciel Outlook un filtre pour en supprimer les copies reçues.
  • Exclusion des adresses email contenant les mots noreply et no-reply.
  • Journalisation de tous les emails, traités et sautés.

2017-08-24

Original :

Bilan de l’exécution du programme pour le compte operations@mycompany.com :

  • 24 réponses automatiques envoyées entre 21:06 et 06:26 (heure locale).
  • La stratégie adoptée par les services et applications Google pour déterminer l’heure exacte des événements (l’heure de réception des messages en l’occurrence) porte plutôt à confusion. Par conséquent, comme Google affirme qu’elle utilise l’heure UTC sur ses services en ligne, une plage horaire plus large sera utilisée pour que l’intervalle 20h-06h (heure locale) soit toujours couvert malgré les éventuels changements (i.e. début, suspension et fin de l’heure d’été). La ligne de code suivante :
    • if (((hour < 6) || (hour >= 20)) && ((threads = GmailApp.search('is:inbox after:' + timeFrom)).length !== 0)) { sera donc modifié en :
    • if (((hour < 6) || (hour >= 19)) && ((threads = GmailApp.search('is:inbox after:' + timeFrom)).length !== 0)) {.
  • Confirmation avec l'équipe des Opérations de la liste des contacts à exclure de la réponse automatique.
  • Désinscription de quelques newsletters via les liens fournis dans les corps de leurs messages respectifs.
  • Modification du message de réponse automatique : l’adresse de modération amine@mycompany.com en Cci au lieu de Cc.
  • Groupement des fichiers du programme dans un même dossier sous Google Drive. Partage avec le compte Google de AMINE (lecture et modification) pour faciliter (centraliser) les consultations et les mises à jour.

2017-08-23 (code)

Original :

Fin du développement de la deuxième version (améliorée).

  • Tests appliqués et réussis :
    • Exclusion des conversations Gmail (avec de nouveau message) au-delà des dernières 10 minutes
    • Exclusions des messages envoyés depuis des adresses avec les alias MAILER-DAEMON@* et postmaster@*
    • Exclusion des messages en provenance des adresses emails de MyCompany (domaine principal + tous les domaines alias)
    • Exclusion des accusés de lecture
    • Exclusion des messages avec des destinations anonymes (undisclosed-recipients)
    • Exclusion des messages déjà traités (i.e. journalisés sur le fichier Autorespond-log)

2017-08-22

Original :

Correction et amélioration du code :

  • Résolu : Interprétation comme expression régulière des chaînes de caractères extraites des fichiers de configuration.
  • Tests, adaptations et corrections.

2017-08-19

Original :

Problèmes en cours de traitement :

  • Le contenu extrait des cellules de la feuille From_regex_blacklist du document Google Spreadsheet Autorespond-config, par la fonction MatchesRegex() ne semble pas être correctement interprété comme étant une expression régulière contre laquelle l’expéditeur devrait être vérifié afin d’exclure les adresses email de MyCompany ainsi que les adresses emails d’administrateur système réservées postmaster@... et mailer-daemon@....
  • La fonction ContainsString() appliquée sur les en-têtes de l’email (i.e. message brut/original) ne détecte pas la présence des expressions report-type=disposition-notification et report-type=delivery-status, ce qui aurait permis l’exclusion des accusés de lecture et des rapports de remise.

2017-08-18 (code)

Original :

  • Définitions complètes des fonctions d’extractions et de vérification de valeurs depuis des documents Google Spreadsheet (configurations et journaux Logs). Le fichier des configuration Autorespond-config contient les feuilles suivantes: To_whitelist, To_regex_whitelist, To_blacklist, To_regex_blacklist, From_whitelist, From_regex_whitelist, From_blacklist, From_regex_blacklist, msgHeaders_blacklist, msgHeaders_regex_blacklist.
  • Un modèle du fichier Autorespond-config sera ultérieurement ajouté au code source sous le format XLSX.

2017-08-23 - Gmail-Autoresponder

  • Test et débogage du code.

2017-08-18 - Gmail-AutoResponder

2017-08-17 (code)

Original :

Optimisation du code :

  • Améliorations apportées ou en cours de développement :
    • Lecture de configurations depuis un document Google Spreadsheets (Autorespond-config),
    • Enregistrement (journalisation) et vérification des informations identifiant les messages traités dans/depuis un document Google Spreadsheets (Autorespond-log),
    • Définition de fonctions génériques pour vérifier les données des en-têtes des messages contre les données extraites des documents précités,
    • Récupération du message de réponse automatique (corps HTML) depuis un emplacement sécurisé sur l’espace d’hébergement web de la société.
    • Utilisation d’une combinaison de configurations pour filtrer les messages à traiter,

2017-08-11

Original :

Test et évaluation de la lecture et écriture de données sur des documents Google Spreadsheet, pour la journalisation des opérations et la lecture de configurations.

2017-08-09

Original :

Améliorations du code : Premiers essais et évaluation d’une journalisation des opérations vers des documents Google Spreadsheet, proprité du même compte Google exécutant le script.

2017-08-08

Original :

La solution finalement retenue et implémentée pour le stockage et l’importation du contenu du corps de message de réponse est l’hébergement d’un fichier HTML sur notre espace web, sous un répertoire protégé par nom d’utilisateur et un mot de passe. Tests et validation.

2017-08-07

Original :

Problématique : Inclusion d’un texte unique dans le corps du message de réponse sans aucune mention d’informations de contact au format texte. La solution envisagée et d’inclure un tableau de contacts sous format Image dans le corps du message. Améliorations étudiées :

  • Inclusion du corps de message au format HTML depuis un fichier externe :
    • Cas d’un fichier texte au format HTML stocké sur Google Drive. Difficulté : Aucune procédure simple et fonctionnelle n’a été trouvée pour lire le contenu brut d’un fichier texte stocké sur Google Drive.
    • Cas d’un document Google Docs exporté au format HTML. Difficulté : Il était possible de récupérer le contenu d’un document Google Docs sous forme de code HTML et l’insérer dans le corps du message de réponse, mais l’image est par défaut bloquée par la plupart des clients de messagerie modernes puisqu’elle est hébergée dans un emplacement externe.
    • Cas d’un fichier texte au format HTML récupéré via un URL : Un fichier contenant le contenu du corps du message au format HTML a été stocké sur notre espace d’hébergement web, et récupéré via l’URL http://mycompany.com/email_body.html. L’image présentant le tableau des contacts y a été codée en Base64 . Difficulté : Le corps du message généré dépasse ainsi la taille maximale autorisée pour un script/projet Google Apps Script.

2017-08-02

Original :

Optimisation du code source du script associé à la boîte email operations@mycompany.com :

  • Exclusion des messages (souvent spam) dont la destination est anonyme (undisclosed-recipients)
  • Exclusion des messages automatiques envoyés depuis des administrateurs de serveurs de messagerie (mailer-daemon, postmaster)
  • Exclusion des accusés de lecture et des rapports de remise.

2017-08-01

Original :

Coordination avec l’équipe des opérations et discussions à propos de la meilleure stratégie à adopter pour la programmation des messages de réponse automatique envisagée hors les heures de travail.

2017-07-29

Original :

  • Véirication des résultats de la première exécution programmée entre 06:00GMT et 20:00GMT.
  • Idées pour amélioration:
    • Exclusion des accusés de lecture. Il va falloir interpréter en-avale les en-têtes dans le code source (en-têtes) de chaque email traité afin de vérifier si le contenu MIME multipart/report est de type : report-type=disposition-notification.
    • Précautions pour assurer une exécution continue du programme jusqu’à la fin de la plage horaire prédéfinie. Cela dépend de plusieurs facteurs :
      • Le temps d’exécution maximal autorisé durant une journée (24h). Les références en ligne à ce sujet (documentation Google officielle comprise) laissent des ambiguïtés : ce serait entre 1h et 6h. Par conséquent, l’intervalle de temps entre chaque exécution du script devra être convenablement choisi selon le temps moyen nécessaire pour le traitement des derniers messages reçus sur le compte mail.
      • Le fuseau horaire et l’heure d’été. Afin d’éviter toute confusion, Google utilise à la base l’heure UTC sur ses plateformes et services, y compris Google Apps Script. Par conséquent, et suite aux changements de l’heure locale, il serait difficile d’inclure des référence horaires dynamiques dans le code source du script ou bien de les modifier manuellement à chaque changement. A concevoir donc, éventuellement, une solution pour adapter automatiquement la plage horaire. En attendant, une plage horaire plus inclusive ; 19h-6h (GMT), soit 20h-7h (GMT+1), sera utilisée.
      • Voir la possibilité de vérifier l’authenticité des expéditeurs (signatures des e-mails…)
      • Externaliser les filtres et les contenus personnalisés pour une meilleure portabilité du code.

2017-07-28 (code)

Original :

  • Fin de développement de la première version du script.
  • Première exécution (automatique) de test pour la boîte email operations@mycompany.com prévue entre 28/07/2017, 20:00GMT et 29/07/2017 06:00GMT.

2017-07-28 - Gmail-Autoresponder

2017-07-27 (code)

XXXXXXXX

  • Specifications and requirements :
    • The script will be configured for steady auto-execution between 8pm and 6am, under every Google user account.

Original :

Continuation de l’étude et développement.

  • Spécifications et cahier de charges :
    • Le script sera configuré pour exécution automatique régulière entre 20h et 6h sur chaque compte utilisateur Google.
    • Il vérifiera les derniers messages reçu. Puisque les messages sont interprétés par Gmail comme des groupes de > discussion (threads), les discussions avec de nouveaux messages (réponses ou transferts) seront inclues.
    • Les messages en provenance des contacts de MyCompany seront exclus.
    • Option : Les messages/discussions avec le libellé Gmail _autoRep seront considérés comme déjà traités et seront > donc systématiquement exclus.
    • Traitement des messages : Envoie du corp de la réponse automatique sous forme de texte riche (HTML) suivie des > informations (date, expéditeur, destinataires, objet) et une citation du contenu du message traité.
    • Attribution du libellé Gmail _autoRep pour marquer le message comme traité.

2017-07-26 (code)

Developing a first prototype of a script to send automatic responses to emails received in a specific timeframe of each day.

2017-07-26 - Gmail-AutoResponder