# Example: e-mail folder monitoring

The ELO Automation Services JavaScript library contains a module for sending and receiving e-mails. This guide will explain how to use ELOas to monitor a mailbox.

Information

This example is not intended to simulate e-mail archiving. There are other modules in our product range that better accomplish this task. Instead, it is intended to serve as a basis for "autoresponders", i.e. programs that automatically trigger an action in response to an e-mail message (for example, a user sends a registration e-mail, after which their account is activated).

# General approach

Before a ruleset can be created to process mailboxes, a mailbox connection must be created in the mail module. As there are many differences and options here, it is not possible to work from a simple configuration list. Instead, you have to create a connect method for each mailbox connection. This must establish a connection with the e-mail server, select the correct mailbox, and read the list of messages.

Every mailbox connection is given a simple, short name - e.g. GMAIL. This name is required at various locations and must be "identifier-compatible", i.e. it must start with a letter and can then contain additional letters or numbers (but no special characters, including letters with accent marks). This name is required at various places in the ruleset and the JavaScript implementation.

# Establishing the connection

The JavaScript library already has a definition in the standard installation for a connection with the name GMAIL. We will use it for the example. As the connection name is used in special functions, you can also define multiple connections in parallel and use them in various rulesets.

The standard function for establishing the GMAIL connection looks like the following:

connectImap_GMAIL: function() {
    var props = new Properties();
    props.setProperty("mail.imap.host", "imap.gmail.com");
    props.setProperty("mail.imap.port", "993");
    props.setProperty("mail.imap.connectiontimeout", "5000");
    props.setProperty("mail.imap.timeout", "5000");
    props.setProperty("mail.imap.socketFactory.class",
                      "javax.net.ssl.SSLSocketFactory");
    props.setProperty("mail.imap.socketFactory.fallback", "false");
    props.setProperty("mail.store.protocol", "imaps");
    var session = Session.getDefaultInstance(props);
    MAIL_STORE = session.getStore("imaps");
    MAIL_STORE.connect("imap.gmail.com",
                       "<USER>@gmail.com",
                       "<PASSWORD>");
    var folder = MAIL_STORE.getDefaultFolder();
    MAIL_INBOX = folder.getFolder("INBOX");
    MAIL_INBOX.open(Folder.READ_WRITE);
    MAIL_MESSAGES = MAIL_INBOX.getMessages();
    MAIL_DELETE_ARCHIVED = false;
},

The example connects to the Googlemail server "imap.gmail.com" at port "993" via an encrypted connection (mail.store.protocol - imaps). This information is entered to a property object. Your own e-mail server may require other values - refer to the e-mail server's documentation for details.

Information

If you set up a Google e-mail account, you must first enable IMAP access to use this method. This is possible under Settings > Forwarding and POP/IMAP > Activate IMAP.

Logon is then performed using the command MAIL_STORE.connect. Enter the server name again, as well as the mailbox user with password.

After logon, the Inbox folder is searched for first. However, any other folders can be monitored, such as Sent:

MAIL_INBOX = folder.getFolder("[Google Mail]/Sent")

Using the command MAIL_INBOX.getMessages(), all e-mails in the folder are read and added to the internal message list. This list will be processed later by calling the ruleset once for each entry in this list.

The variable MAIL_DELETE_ARCHIVED determines whether the ruleset is allowed to delete messages or mark them as processed after successful processing. If it is set to "false", as is preconfigured, the message status is not changed. This is especially practical in the testing phase, as it is not necessary to constantly create new e-mails. In production, this entry is normally set to "true".

# Create ruleset

A simple ruleset to process the mailbox content consists of two main parts: the definition of the search and the script to process the e-mails.

The search is defined as follows:

<search>
<name>"MAILBOX_GMAIL"</name>
<value>"ARCPATH:¶IMAP"</value>
<mask>2</mask>
<max>200</max>
</search>

The search name "MAILBOX_GMAIL" signalizes that this is not a normal repository search, but rather a search of a mailbox with the connection name GMAIL. The created ELO documents are filed to the "IMAP" folder (via ARCPATH:¶IMAP) and created with form 2 (e-mail in a standard ELO repository). Normally, the number of results is no longer relevant, but it should still be entered to prevent an error message in the designer.

The script to run is essentially determined by the required function. A simple script could look like the following:

<script>
    log.debug("Process Mailbox: " + NAME);
    OBJDESC = mail.getBodyText(MAIL_MESSAGE);
    ELOOUTL1 = mail.getSender(MAIL_MESSAGE);
    ELOOUTL2 = mail.getRecipients(MAIL_MESSAGE, "¶");
    EM_WRITE_CHANGED = true;
    MAIL_ALLOW_DELETE = true;
</script>

When the script is run, the message is available in the MAIL_MESSAGE variable. Standard values like e-mail text, sender, and recipient can be read from here. To simplify the process, the mail module provides the help routines getBodyText, getSender, and getRecipients.

The subject is automatically used as the short name (NAME). The body of the e-mail is entered to the extra text, and the sender and recipient are transferred to their corresponding metadata fields. Last, the message is marked as processed as deleted via MAIL_ALLOW_DELETE.

The complete example will then look like the following:

<ruleset>
<base>
<name>Mailbox</name>
<search>
<name>"MAILBOX_GMAIL"</name>
<value>"ARCPATH:¶IMAP"</value>
<mask>2</mask>
<max>200</max>
</search>
<interval>10M</interval>
</base>
<rule>
<name>List</name>
<condition></condition>
<script>
    log.debug("Process Mailbox: " + NAME);
    OBJDESC = mail.getBodyText(MAIL_MESSAGE);
    ELOOUTL1 = mail.getSender(MAIL_MESSAGE);
    ELOOUTL2 = mail.getRecipients(MAIL_MESSAGE, "¶");
    EM_WRITE_CHANGED = true;
    MAIL_ALLOW_DELETE = true;
</script>
</rule>
<rule>
<name>Global Error Rule</name>
<condition>OnError</condition>
<script></script>
</rule>
</ruleset>

# Monitored processing

This simple example has a significant disadvantage: when an e-mail has already been marked as "processed" or deleted, and the process is canceled before the data could be saved in the repository, a data set will remain unprocessed. This problem can be completely avoided by working with a two-level approach: a new e-mail is initially only saved in ELO, but not deleted. The e-mail is only deleted if a later run finds that it already exists in ELO.

This approach has two requirements: the e-mail must be uniquely identifiable, and the method must check during processing whether the e-mail already exists in the repository. The first condition is easy to meet: every e-mail has an internal mail ID. This can be saved to a metadata field in ELO (such as in the default e-mail form in the field ELOOUTL3, which is intended for the mail ID). The second condition can easily be met with a help routine from the ELOix module: ix.lookupIndexByLine.

The changed script will then look like the following:

<script>
    log.debug("Process Mailbox: " + NAME);
    // if the message is already in the repository: then delete..
    var msgId = MAIL_MESSAGE.messageID;
    if (ix.lookupIndexByLine(EM_SEARCHMASK, "ELOOUTL3", msgId) != 0) {
        log.debug("E-mail already exists in repository,
                  ignore or delete");
        MAIL_ALLOW_DELETE = true;
    }   else {
        OBJDESC = mail.getBodyText(MAIL_MESSAGE);
        ELOOUTL1 = mail.getSender(MAIL_MESSAGE);
        ELOOUTL2 = mail.getRecipients(MAIL_MESSAGE, "¶");
        ELOOUTL3 = msgId;
        EM_WRITE_CHANGED = true;
    }
</script>

# Marking instead of deleting

In the standard implementation, a processed e-mail is deleted from the mailbox. This is undesirable in some cases. However, a marker can be set for the messages instead. A possible candidate is the "read" flag. A processed e-mail message is set as "read" by ELOas and thus differs from a new e-mail. In this special case, additional methods have to be defined in the mail JavaScript library in addition to the connectImap method:

nextImap_GMAIL(): This function switches to the next message. In this example, you have to check whether an e-mail has already been marked as read, and can skip over it if needed.

finalizeImap_GMAIL(): This function marks the processed message. In the standard implementation, the message is deleted. In our example, however, it should only be marked as read.

# nextImap_GMAIL

This function switches to the next message. It goes through the list of messages in sequence. The current position is saved in the MAIL_POINTER variable. If a message has already been marked as read, it is skipped. At the first unread message, it is activated (meaning, copied into the MAIL_MESSAGE variable) and the value "true" is returned. If there are no further messages, a "false" value is returned. ELOas finishes processing this ruleset and then switches to the next.

nextImap_GMAIL: function() {
    for (;;) {
        if (MAIL_POINTER >= MAIL_MESSAGES.length) {
            return false;
        }
        MAIL_MESSAGE = MAIL_MESSAGES[MAIL_POINTER];
        var flags = MAIL_MESSAGE.getFlags();
        if (flags.contains(Flags.Flag.SEEN)) {
            MAIL_POINTER++;
            continue;
        }
        MAIL_ALLOW_DELETE = false;
        MAIL_POINTER++;
        return true;
    }
    return false;
},

In addition to switching to the next message, initialization takes place: the variable MAIL_ALLOW_DELETE is set to false. This value should only be set to true when an object has been processed within the ruleset. In this case, the e-mail is marked as processed in the finalizeImap method.

# finalizeImap_GMAIL

The finalizeImap_GMAIL function must mark an e-mail as processed. This is done by setting the SEEN flag. However, it may only be set if the connect method allows it at all (MAIL_DELETE_ARCHIVED), and the ruleset has marked the current e-mail message as archived (MAIL_ALLOW_DELETE).

finalizeImap_GMAIL: function() {
    if (MAIL_DELETE_ARCHIVED &amp;&amp; MAIL_ALLOW_DELETE) {
        message.setFlag(Flags.Flag.SEEN, true);
    }
},
Last updated: September 26, 2023 at 7:46 AM