Is it possible to script ThunderBird filters

21
2014-02
  • Nifle

    Is it possible to write your own filters for Thunderbird, I don't care what language is used.

    I want to be able to do a full analysis of the email (headers and text) and take certain actions based on the content.

    Bonus points if I'm able to compare the current email with older received messages (mainly to remove duplicate log messages)

  • Answers
  • minghua

    Actually it is doable with FiltaQuilla. (Tried FiltaQuilla 1.1.0, with Thunderbird 3.1.9).

    Below is a test script put under "javascript action with body" supplied by FiltaQuilla. It does show all the header properties and the body in the error console.

    Once those data fields are available to the code, it needs to traverse through messenger folders and messages to compare them. Maybe using the "threadParent" property on the hdr can simplify the logic, refer to nsIMsgDBHdr on MDC for a hint. In the code, extra properties can be set. For example, set "hdr.myvar='1234'" in the code for duplicated messages, then in a later filter match myvar to 1234 and do a normal action to delete them.

    Since this script is the action part of a filter, the condition part of the filter needs to match all messages of interest. In this test the script is arranged after an action to copy the message to a local folder, that may or may not be necessary but it does download the message body to the local machine on an IMAP account so the action will do have a body to access.

    //FiltaQuilla filter action script: 
    for (let index = 0; index < msgHdrs.length; index++)
    { //get msg hdr:
      let hdr = msgHdrs.queryElementAt(index, Ci.nsIMsgDBHdr);
    
      //show subject: get it in two ways
      let s = hdr.getStringProperty("subject");
      if ( s != undefined && s.length != undefined && s.length > 0)
          Cu.reportError("  flt log1: " + s.length + " " + s);
      Cu.reportError("  flt log2: " + hdr.subject );
    
      //show all properties of hdr: uncomment to show.
      //let e = hdr.propertyEnumerator;
      //while ( e.hasMore() ) Cu.reportError("  flt log3: " + e.getNext() ); 
    
      //get body: from an example on MDC: getMessageBody(aMessageHeader)
      //Actions configured in the filter: Without [1], it takes long long time. 
      //    [1] copy msg to a local folder. 
      //    [2] "Javascript Action with Body" with this script. 
      { let messenger = Components.classes["@mozilla.org/messenger;1"]
                          .createInstance(Components.interfaces.nsIMessenger);
        let listener = Components.classes["@mozilla.org/network/sync-stream-listener;1"]
                          .createInstance(Components.interfaces.nsISyncStreamListener);
        let uri = hdr.folder.getUriForMsg(hdr);
        messenger.messageServiceFromURI(uri)
                    .streamMessage(uri, listener, null, null, false, "");
        let folder = hdr.folder;
        let body = folder.getMsgTextFromStream(listener.inputStream,
                                  hdr.Charset, 65536, 32768, false, true, { });
        //show body:
        Cu.reportError("  flt log4: " + body.length + " " + body);
      }
    }
    
  • mivk

    No, this doesn't seem to be possible.

    Such processing is probably easiest to do on the mail server, with stuff like procmail, Courier maildrop, amavisd-new, etc.

    Amavisd-new is specifically designed to pass mail to external processing programs before delivery, usually anti-virus and anti-spam daemons.

    If you cannot do it on the server side, you would need to write an extension, but that is probably far too much work.

    Finally, maybe you can satisfy a good part of your needs with the FiltaQuilla extension, which supports regular expressions, running external programs, and javascript actions. You can find more info on the author's page.

    Personally, if I didn't have access to the mail server and if FiltaQuilla didn't satisfy my needs, I would chose to set up a local Postfix / IMAP mail server fetching the mail from the ISP and processing it before delivery with my custom scripts. It should be much easier than writing a TB extension. But it may be overkill for your needs.

  • RedGrittyBrick

    You could look for a POP3/IMAP proxy that has the scriptable filtering capabilities you need. It could be a proxy which runs on the same PC as Thunderbird.

  • G. Harrington

    With the extension: http://mesquilla.com/extensions/filtaquilla/

    you can create JavaScript and/or external files to execute, so it looks like a solid "maybe"


  • Related Question

    How to combine filter rules in message filters in Thunderbird to create more advanced filter criteria?
  • Piotr Dobrogost

    In Thunderbird one can choose either Match all of the following or Match any of the following for a given set of filter rules. There's no way to mix OR and AND logical operators freely to create more complex criteria like ((A AND B) OR (C AND D)) AND E.
    Is there any plug-in allowing to achieve the above?


  • Related Answers
  • JDB

    I can't seem to find a good extension to do this, but the quick search toolbar would function just as well if you're just searching over a single folder.

    For instance - if I wanted to create criteria for all messages from Tommy and Billy but not those with Fwd in the subject, as well as messages from Sandra including forwards I would set up a search (using the quick search toolbar with it set to search by expression):

    ((from:Tommy OR from:Billy) AND -subject:Fwd) OR (from:Sandra)

    However, with multiple folders, this is not possible.

    J

  • random

    I needed to filter a bunch of different subjects coming from one person, so my solution was this:

    1. Added a filter to match any of the following, and added any of the subjects I wanted.
    2. The action I set it to perform is to set the priority to lowest.
    3. Then I added a new filter to match all of the following: priority is lowest, and from contains (the email address I wanted to filter from).
    4. The action I set was to move to a certain folder.

    One thing you need to make sure of is that the first filer is higher up on the list so it gets performed first. I know it's not the most elegant solution but it can be expanded upon to fit your needs.

  • minghua

    With FiltaQuilla, and some javascript actions/rules, it is doable. However, here it is an example with some modifications to the FiltaQuilla source so as to simplify the process.

    The example here will do three stages of filtering: marking - that only classifies the emails. Then, modifying - that does some logical computation and modifies the email subject or headers. And, action - doing the actual action. Each of the stages will involve editing filters in Thunderbird filter dialog normally, but with added options by FiltaQuilla. The stage 1 and 2 save their results in a newly added email header field, and stage 2 and 3 use the results in the field to do some logic before normal actions.

    Steps:

    Install FiltaQuilla

    It looks many functions are only working with Thunderbird 3.1. Thus upgrade to TB 3.1 first. Then install FiltaQuilla.

    In FiltaQuilla preference tab, enable "Suffix to Subject", and "Javascript Action".

    Modify FiltaQuilla Source

    Edit "[email protected]/content/filtaquilla.js" in the extension directory so that it looks like this:

    // Suffix to subject
    self.subjectSuffix =
    {
      ...
      apply: function(aMsgHdrs, aActionValue, aListener, aType, aMsgWindow)
      {
        for (var i = 0; i < aMsgHdrs.length; i++)
        {
          var msgHdr = aMsgHdrs.queryElementAt(i, Ci.nsIMsgDBHdr);
          ////var appSubject = _mimeAppend(aActionValue, msgHdr.subject, false);
          ////msgHdr.subject = appSubject;
          var headerName = "mykeywords";
          var headerValue = msgHdr.getStringProperty(headerName);
          msgHdr.setStringProperty(headerName, headerValue + " " + aActionValue);
          headerValue = msgHdr.getStringProperty(headerName);
            // Cu.reportError("chg : " + headerName + " : " + headerValue);
        }
      },
    

    The code changes the original action of "Suffix to Subject" into that it will add the suffixed strings into a new header named "mykeywords". This new header field will be used to keep the result from the first two stages of filtering in the forms of string words.

    This source code change is to reuse the "Suffix" action since usually suffixing to a subject is not very useful. Thus reusing its internal guts would not affect the usability of FiltaQuilla a lot. If not doing this, an official feature request should be post to FiltaQuilla creator to add the feature you want, or you'll need to write a bit more Javascript code in the filter condition as Javasctipt condition.

    Create Filter Rules for Marking

    An example is to create a series of rules each will only have one action: "Suffix to Subject", but the suffixed words will each identify what the result it has got. For example, classify the emails according to where they are from by suffixing words "company-A", "company-B", ..., etc. Remember that these words "company-A", "company-B", etc., will be concatenated into the "mykeywords" header field.

    Place these rules to the beginning of filter rules list.

    Create Filter Rules for Logical Modification

    In filter rules header-field drop box, use "Customize" to add "mykeywords" to the list. Then choose "mykeywords contains company-" for condition in filter dialog.

    Choose "Javascript Action" in action section. Add some code like this:

    for (let index = 0; index < msgHdrs.length; index++)
    {
      let hdr = msgHdrs.queryElementAt(index, Ci.nsIMsgDBHdr);
      let s = hdr.getStringProperty("mykeywords");
      let v = s.split("company-"); /* result words are in v[] now */
      let r = ""; /* logic conversion result */
      let cnt = 0;
      if ( v != undefined && v.length != undefined && v.length > 0) {
        let lastVN = 0;
        for(var i=v.length -1; i>=0; i--) {
          let ss = v[i];
          if ( ss.length > 1 ) {
              ss = ss.substring(0);
              /* convert company A into VIP, B into NORMAL, C into IGNORE. 
               * Assume the marking section starts with A,B, then C thus 
               * C gets parsed first, then A and B. 
               */
              if (ss.search(/A/) == 0)  { ss = "V"; lastVN = 1; } /*VIP*/
              else if (ss.search(/B/) == 0 ) { ss = "N"; lastVN = 1; } /*NORMAL*/
              else if (ss.search(/C/) == 0   ) { ss = "IGNORE"; }
              /* prepend subject line */
              if ( cnt == 0 ) { r = ss + "] ";
              } else { if (lastVN == 0) r = ss + " " + r; else  r = ss + r;  }
              cnt ++;
          }
        } /* for(var i=v.length -1; */
        if ( cnt > 0 ) { r = "[" + r; }
      } /* if ( v != undefined && */
      hdr.subject = r + hdr.subject;
    } /* for (let index = 0; */
    

    At this point, all the marking results can be accessed by the script from "mykeywords" header field. Just parse the string, then any logic can be applied after the parsing to achieve the logic result you like. For example, you can apply if "A" and "B", add "result-AB" to "mykeywords", etc. Then in the next stage to check "mykeywords contains result-AB" for actually meaning for "A and B".

    The above example also shows that the "[VN IGNORE]" can be prepended to the subject line to indicate which of the three companies have been involved in the subject email.

    Create Filter Rules for Action

    Now create the action rule based on the values contained in "mykeywords". This will be just normal settings.

    Notes:

    The FiltaQuilla supports javascripts in the condition section. Thus if you don't want to change the extension source code, you'll need to write a bit more code in the condition section of filter dialog. With that code any logic computation can be done too.

    Check out FiltaQuilla site for lot more information.

  • Sam Brightman

    Maybe you could use tags for each sub-clause of the expression. if ((from:Tommy OR from:Billy) then tag with "TomAndBilly" tag. Then filter looking for not subject:Fwd AND tag TomAndBilly. I haven't tried this, I guess it won't work if TB won't filter more than once. Maybe you can also force it to go more than once by bouncing the mail to different folders, or even accounts (but then it's starting to get really ugly).

    I heard better tagging was due in TB3, but it doesn't seem evident in the UI.