The attached script might be of interest to some. It's written in a JSON style, shows regexp literals in action and uses a HOST variable to improve performance by avoiding multiple calls to the host scripting environment. (In practice, I've only noticed a significant gain with this technique when using a high number of calls to Toolkit.HashMD5()).
The script acts on notification messages from a wordpress based forum that outputs broken 8bit text/html and contain horrific formatting - the most obvious being many spurious line breaks. The task is to output a new, cleaner message with a References header for threading based on the forum topic-id. Mailtraq's database feature (.ini files in \db) is used to keep track of the topic ids.
- Code: Select all
<@LANGUAGE=JavaScript@>
<%
/*
wp-forum-notify.mtq
Version : 0.3 17 April 2013
Author : Martin Clayton
Description : wordpress forum message-notification message rewrite
expects broken text/html input
Outputs new message:
- Subject set to forum topic name
- From header name set to topic author [optional]
- add References header for threading
o use forum URL to identify topic-id
o db stores message-id of message with first topic instance
- remove multiple "<br />" in body
Trigger : Mail inbound
Filter : - reverse-path &
- Subject, eg "Members' area Notification"
Help : http://forum.mailtraq.com/viewforum.php?f=15 "Example of json style javascript"
*/
var HOST = {
// host environment object : local copy to prevent context switching performance hit
server : Server,
request : Request,
toolkit : this.server.Toolkit
}, CFG = {
// output message format
sendtoReversePath : HOST.server.AddressOf("postmaster"),
sendtoRecipient : "Wordpress 'forum' member <my.local.newsgroup@example.com>",
// change "Forum Admin" to Topic author [but leave email addresss unchanged]
sendtoChangeFromName : true,
// if true, prefix name with...
sendtoChangeFromPrefix : "[HB] ",
dbFile : "wp-forum-notify.db",
// NB input line is left+right trimmed prior to regexp evaluation
regexPost : /^The following comment was posted in the discussion <strong>(.*?)<\/strong>\.$/i,
regexTopicNew : /^A new discussion called <strong>(.*?)<\/strong> was started, the comment is as follows\.$/i,
regexAuthor : /^Post by: <strong>(.*?)<\/strong>$/i,
regexTopicId : /^<a href="http:\/\/example.net\/wordpress\/forum\/comments.php\?DiscussionID=(.*?)&page=(.*?)#Item_(.*?)">.*$/i
}, MSG = {
init : function () {
this.lineCount = HOST.request.LineCount;
this.hdrMessageId = HOST.request.MessageId;
this.hdrSubject = HOST.request.GetHeader("Subject");
this.hdrFrom = HOST.request.GetHeader("From");
this.hdrReplyTo = HOST.request.GetHeader("Reply-To");
this.msgBody = '';
this.topicNew = false;
this.topicName = null;
this.topicId = null;
this.topicAuthor = null;
return this.lineCount;
},
parse : function () {
var line_i = 0,
body = false,
lineFull = '',
lineTrimmed = '';
while(line_i < this.lineCount) {
lineFull = HOST.request.GetLine(line_i);
lineTrimmed = UTILS.trim(lineFull);
if (body === false) {
if (lineTrimmed === '') {
body = true;
}
} else {
// look for (new) topic name [used in message Subject]
if (!this.topicName) {
if (CFG.regexPost.test(lineTrimmed)) {
this.topicName = lineTrimmed.replace(CFG.regexPost, "$1");
} else if (CFG.regexTopicNew.test(lineTrimmed)) {
this.topicNew = true;
this.topicName = lineTrimmed.replace(CFG.regexTopicNew, "$1");
}
}
if ((!this.topicAuthor) && CFG.regexAuthor.test(lineTrimmed)) {
this.topicAuthor = lineTrimmed.replace(CFG.regexAuthor, "$1");
}
if ((!this.topicId) && CFG.regexTopicId.test(lineTrimmed)) {
this.topicId = lineTrimmed.replace(CFG.regexTopicId, "$1");
}
// deduplicate "<br />" and store untrimmed line
this.msgBody += lineFull.replace(/(<br \/>)+/ig, "<br />") + "\r\n";
}
line_i++;
}
// Store Message-ID in db if we don't already have one for the Topic
if (this.topicId) {
if (!this.topicRef() && this.hdrMessageId) {
DB.save(this.topicId, "mid", this.hdrMessageId);
}
}
return;
},
topicRef : function () {
var reference = DB.getValue(this.topicId, "mid");
// // rewrite myself so DB lookup can only run once
// this.topicRef = function () {
// return reference;
// };
return reference;
}
}, OUTMSG = {
send : function () {
var sender = CFG.sendtoReversePath || HOST.server.AddressOf("postmaster"),
rcpts = '',
headersAll = '',
hdrSubject = MSG.hdrSubject || "[no subject]",
hdrFrom = MSG.hdrFrom,
hdrReplyTo = MSG.hdrReplyTo || hdrFrom,
attachments = null,
hdrMessageId = '',
hdrFromAddress = '';
rcpts = HOST.toolkit.ExtractEmailAddresses(CFG.sendtoRecipient);
// Subject
if (MSG.topicName) {
hdrSubject = (MSG.topicNew) ? MSG.topicName : "Re: " + MSG.topicName;
}
// From
if (CFG.sendtoChangeFromName && MSG.topicAuthor) {
hdrFromAddress = HOST.toolkit.ExtractEmailAddresses(hdrFrom);
hdrFrom = CFG.sendtoChangeFromPrefix + MSG.topicAuthor + " <" + hdrFromAddress + ">";
}
// Source message format:
// - Content-transfer-encoding: 8bit
// - Content-type: text/html; charset=utf-8
// MessageSend() seems to require QP
// (Content-Type with 'charset=utf-8' breaks formatting)
headersAll = "To: " + CFG.sendtoRecipient + "\r\n" +
"Subject: " + hdrSubject + "\r\n" +
"Date: " + HOST.toolkit.MessageTimestamp + "\r\n" +
"From: " + hdrFrom + "\r\n" +
"Reply-To: " + hdrReplyTo + "\r\n" +
"MIME-Version: 1.0" + "\r\n" +
"Content-Type: text/html" + "\r\n" +
"Content-Transfer-Encoding: quoted-printable";
// References header for basic threading
if (MSG.topicRef()) {
headersAll += "\r\n" + "References: <" + MSG.topicRef() + ">";
}
HOST.server.MessageSend(sender, rcpts, headersAll, MSG.msgBody, attachments, hdrMessageId);
return;
}
}, DB = {
save : function (record, field, value) {
HOST.server.DBWrite(CFG.dbFile, record, field, value);
return;
},
getValue : function (record, field) {
var value = '';
if (HOST.server.DBRecordExists(CFG.dbFile, record)) {
value = HOST.server.DBRead(CFG.dbFile, record, field);
}
return value;
}
}, UTILS = {
trim : function (input) {
return ('' + input).replace(/^\s+|\s+$/g, '');
}
};
if (MSG.init()) {
MSG.parse();
OUTMSG.send();
}
%>