Lotusscript: Mail Notification Class

Back in August 2009, I posted a small class I had created to make it easier for me to create mail notifications in my Lotusscript agents. Since then I have improved on it, and I wanted to post the latest version. One recent feature I added is to send external mail to the internet that looks like it is coming from a different user. This is often needed when you have an agent signed with a developer/admin ID, but you want the mail to look like it is coming from a particular department or user.
This is done by using the (unsupported) method of saving the document in mail.box instead of using doc.Send() to mail it directly. What I found is that you should use mail.box on the same server as the agent is running on, you can not access mail.box on a different server. The code reflect this.

Other new features are support for doclinks, attachments and an option to display a warning message at the end that the mail comes from an un-monitored role account. This warning is actually turn on by default.

Option Public
Option Declare
Class NotesMail
Public maildoc As NotesDocument
Public body As NotesRichTextItem
Private p_subject As String
Private p_sendto List As String
Private p_copyto List As String
Private p_blindcopyto List As String
Private p_principal As String
Public NoReply As Integer
Public mailbox As NotesDatabase
Public Sub New()
Dim session As New NotesSession
Dim mailservername As String
mailservername = session.Currentdatabase.Server
' Using mail.box directly is unsupported, but is the
' only way to make the mail look like it is actually
' sent from another address, in our case the principal.
Set mailbox = New NotesDatabase(mailservername,"mail.box")
If mailbox.Isopen = False Then
Print "mail.box on " & mailservername & " could not be opened"
Exit Sub
End If
Set me.maildoc = New NotesDocument(mailbox)
Call me.maildoc.ReplaceItemValue("Form","Memo")
Set me.body = New NotesRichTextItem(maildoc,"Body")
me.p_subject = ""
me.p_principal = ""
' Empty lists for addresses
Erase me.p_sendto
Erase me.p_copyto
Erase me.p_blindcopyto
me.NoReply = False ' Default is to add a disclaimer to the end
End Sub
Public Property Set Subject As String
me.p_subject = FullTrim(Subject)
End Property
Public Property Get Subject As String
Subject = me.p_subject
End Property
Public Property Set Principal As String
me.p_principal = FullTrim(Principal)
End Property
Public Property Get Principal As String
Principal = me.p_principal
End Property
'*** Recipient address (mailto) functions ***
Public Property Set MailTo As String
me.p_sendto(FullTrim(MailTo)) = FullTrim(MailTo)
End Property
Public Property Get MailTo As String ' Get the first address only
ForAll mto In me.p_sendto
MailTo = mto
Exit ForAll
End ForAll
End Property
Public Property Set SendTo As String ' Alias for MailTo
MailTo = SendTo
End Property
Public Property Get SendTo As String ' Alias for MailTo
SendTo = MailTo
End Property
Public Sub AddMailTo(address As String) ' Additional address
me.p_sendto(address) = address
End Sub
Public Sub RemoveMailTo(address As String) ' Remove address
Erase me.p_sendto(address)
End Sub
' *** Functions for CC address ***
Public Property Set MailCC As String
me.p_copyto(FullTrim(MailCC)) = FullTrim(MailCC)
End Property
Public Property Get MailCC As String ' Get the first address only
ForAll mcc In me.p_copyto
MailCC = mcc
Exit ForAll
End ForAll
End Property
Public Sub AddMailCC(address As String)
me.p_copyto(address) = address
End Sub
Public Sub RemoveMailCC(address As String)
Erase me.p_copyto(address)
End Sub
' *** Functions for BCC address ***
Public Sub AddMailBCC(address As String)
me.p_blindcopyto(address) = address
End Sub
Public Property Set MailBCC As String
me.p_blindcopyto(FullTrim(MailBCC)) = FullTrim(MailBCC)
End Property
Public Property Get MailBCC As String ' Get the first address only
ForAll bcc In me.p_blindcopyto
MailBCC = bcc
Exit ForAll
End ForAll
End Property
Public Sub RemoveMailBCC(address As String)
Erase me.p_blindcopyto(address)
End Sub
' *** Functions for email body ***
Public Sub AppendText(bodytext As String)
Call me.body.AppendText(bodytext)
End Sub
Public Sub AppendDocLink(doc As NotesDocument, comment As String, linktext As String)
If FullTrim(linktext) = "" Then
Call me.body.AppendDocLink(doc, comment)
Else
Call me.body.AppendDocLink(doc, comment, linktext)
End If
End Sub
Public Sub AppendNewLine(cnt As Integer)
Call me.body.AddNewline(cnt)
End Sub
Public Sub AddNewLine(cnt As Integer)
Call me.body.AddNewline(cnt)
End Sub
Public Sub AttachFile(filename As String)
Call me.body.EmbedObject(1454,"",filename)
End Sub
' *** Send the mail
Public Sub Send()
Dim session As New NotesSession
Dim richStyle As NotesRichTextStyle
If me.subject<>"" Then
maildoc.Subject = me.subject
End If
If ListItemCount(me.p_sendto)>0 Then
maildoc.SendTo = ListToArray(me.p_sendto)
maildoc.Recipients = ListToArray(me.p_sendto)
End If
If ListItemCount(me.p_copyto)>0 Then
maildoc.CopyTo = ListToArray(me.p_copyto)
End If
If ListItemCount(me.p_blindcopyto)>0 Then
maildoc.BlindCopyTo = ListToArray(me.p_blindcopyto)
End If
If me.p_principal<>"" Then
Call maildoc.ReplaceItemValue("Principal", me.p_principal)
' If principal is set, we want to fix so mail looks like
' it is coming from that address, need to set these fields
Call maildoc.ReplaceItemValue("From", me.p_principal)
Call maildoc.ReplaceItemValue("Sender", me.p_principal)
Call maildoc.ReplaceItemValue("ReplyTo", me.p_principal)
Call maildoc.ReplaceItemValue("SMTPOriginator", me.p_principal)
End If
' If NoReply is set (default), append red warning...
If NoReply = True Then
Set richStyle = session.CreateRichTextStyle
richStyle.NotesFont = 4
richStyle.NotesColor = 2
richStyle.Bold = True
Call me.body.AppendStyle(richStyle)
Call me.body.AddNewLine(1)
Call me.body.AppendText("*** DO NOT REPLY TO THE SENDER OF THIS MESSAGE! ***")
Call me.body.AddNewLine(1)
Call me.body.AppendText("*** IT IS AN AUTOMATED SYSTEM MAIL ***")
End If
Call maildoc.ReplaceItemValue("PostedDate",Now())
If me.p_principal<>"" Then
Call maildoc.Save(True,False) ' Save in mail.box
Else
Call maildoc.Send(True) ' Send mail normally
End If
End Sub
' *** Private functions called from within the class ***
' *** Convert list to array
Private Function ListToArray(textlist As Variant) As Variant
Dim i As Integer
Dim temparray() As String
ReDim temparray(0) As String
ForAll t In textlist
temparray(UBound(temparray)) = t
ReDim Preserve temparray(UBound(temparray)+1) As String
End ForAll
ListToArray = FullTrim(temparray)
End Function
' *** Count items in a list
Private Function ListItemCount(textlist As Variant) As Integer
Dim cnt As Integer
cnt = 0
ForAll t In textlist
cnt = cnt + 1
End ForAll
ListItemCount = cnt
End Function
End Class

Update: Please don’t use the email addresses in the example code above to actually send email…

Like this:

12 thoughts on “Lotusscript: Mail Notification Class”

Late reply to this blogpost, but found it useful.
One thing to note: If there is no field ‘PostedDate’, it does NOT show up in traveler-mails (e.g. iPad).
It does in the regular Notes mail, but not via Traveler. Weird, but easy solution.
So in your case, add ‘maildoc.PostedDate=now’ in the ‘Send’ section.

Thanks a lot for this class I am using this class for sending mail from script. I am new to Lotus Script and trying to learn this part. I have a scenario where I have to send files to users from 2 different mail id’s. If there is a failure in sending mail then I need to create an agent which will run to read this failure mails and call an different service and send the details. Can you please help me regarding this issue.

Define “failure”. Are you talking about a non-delivery message, like a 4xx or 5xx error?

Those messages will be sent to the mailbox that you set as sender (Principal) in your code. You simply write a Lotusscript agent to read the mail box, locate those emails, parse the content and create/send your mail notifications. You could also create agents in the two mailboxes set to run “before mail is received”.

The class does currently not support HTML (MIME) email, but you can add that if you like. if you do, please consider sharing your code back with the community, as a thanks for the code you been getting for free.

By Failure I meant delivery failure of any sort like be it user not present or anything. Now if I set a different Reply to will it be sent to that mail box or the sender mail box? One more thing is that in my scenario I have 2 domino server. One where the service will run to send mail and another domino is the actual mail server. So if I place this agent in the first domino server will I be able to read the mail box?

I tried to enhance this class to support HTML email but for that when I add a simple private field even I am getting this Error: ” Type mismatch on external name: NOTESMAIL”. I am pretty new and don’t know how to get the detail log.

Mail is mail, it all works the same, no matter what software you use to send it. The receiving SMTP server will reject the email, either at the initial connection or after processing the email, and you will get a non-delivery message back, to whatever address you set as sender, or if you set “return-path” it will be sent there instead.http://en.wikipedia.org/wiki/Bounce_message

Mail boxes in Domino is like any other database, you can read one on server B even if your code is on server A. You may have to set up the servers as trusted if they are not on the same Named Notes Network.

The error you get is because the signature of the class has changed, you need to perform a “Recompile all Lotusscript” to rebuild the class signature and update all calling code.

But to create HTML mail, you should take a look at the NotesMIMEEntity class…

I have made this class to send HTML mail. Please re-factor it if necessary.

Public maildoc As NotesDocument
Public textBody As String
Public htmlBody As String
Private p_replyto As String
Private p_subject As String
Private p_sendto List As String
Private p_copyto List As String
Private p_blindcopyto List As String
Private p_principal As String
Public NoReply As Integer
‘Public IsHTML As Integer
Public mailbox As NotesDatabase
Public bodyMIME As NotesMIMEEntity
Public header As NotesMIMEHeader
Private session As NotesSession

Public Sub New()
Set session = New NotesSession
Dim mailservername As String
session.Convertmime = False
mailservername = session.Currentdatabase.Server
‘ Using mail.box directly is unsupported, but is the
‘ only way to make the mail look like it is actually
‘ sent from another address, in our case the principal.
Set mailbox = New NotesDatabase(mailservername,”mail.box”)
If mailbox.Isopen = False Then
Print “mail.box on ” & mailservername & ” could not be opened”
Exit Sub
End If
Set me.maildoc = New NotesDocument(mailbox)
Call me.maildoc.ReplaceItemValue(“Form”,”Memo”)
Set me.bodyMIME = me.maildoc.Createmimeentity

‘ *** Send the mail
Public Sub Send()
Dim session As New NotesSession
Dim richStyle As NotesRichTextStyle
If me.subject”” Then
maildoc.Subject = me.subject
End If
If ListItemCount(me.p_sendto)>0 Then
maildoc.SendTo = ListToArray(me.p_sendto)
maildoc.Recipients = ListToArray(me.p_sendto)
End If
If ListItemCount(me.p_copyto)>0 Then
maildoc.CopyTo = ListToArray(me.p_copyto)
End If
If ListItemCount(me.p_blindcopyto)>0 Then
maildoc.BlindCopyTo = ListToArray(me.p_blindcopyto)
End If
If me.p_principal”” Then
Call maildoc.ReplaceItemValue(“Principal”, me.p_principal)
‘ If principal is set, we want to fix so mail looks like
‘ it is coming from that address, need to set these fields
Call maildoc.ReplaceItemValue(“From”, me.p_principal)
Call maildoc.ReplaceItemValue(“Sender”, me.p_principal)
Call maildoc.ReplaceItemValue(“ReplyTo”, me.p_replyto)
Call maildoc.ReplaceItemValue(“SMTPOriginator”, me.p_principal)
End If
‘ If NoReply is set (default), append red warning…
‘If NoReply = True Then
‘Set richStyle = session.CreateRichTextStyle
‘richStyle.NotesFont = 4
‘richStyle.NotesColor = 2
‘richStyle.Bold = True
‘Call me.body.AppendStyle(richStyle)
‘Call me.body.AddNewLine(1)
‘Call me.body.AppendText(“*** DO NOT REPLY TO THE SENDER OF THIS MESSAGE! ***”)
‘Call me.body.AddNewLine(1)
‘Call me.body.AppendText(“*** IT IS AN AUTOMATED SYSTEM MAIL ***”)
‘End If
Call maildoc.ReplaceItemValue(“PostedDate”,Now())
If me.p_principal”” Then
Call maildoc.Save(True,False) ‘ Save in mail.box
Else
Call maildoc.Send(True) ‘ Send mail normally
End If
End Sub

‘ *** Send the mail
Public Sub SendMIME(bodyText As NotesStream, fileName As String)
On Error GoTo errHandler
Dim session As New NotesSession
Dim bodyChild As NotesMIMEEntity
Dim stream As NotesStream
session.ConvertMIME = False
If me.subject”” Then
maildoc.Subject = me.subject
End If
If ListItemCount(me.p_sendto)>0 Then
maildoc.SendTo = ListToArray(me.p_sendto)
maildoc.Recipients = ListToArray(me.p_sendto)
End If
If ListItemCount(me.p_copyto)>0 Then
maildoc.CopyTo = ListToArray(me.p_copyto)
End If
If ListItemCount(me.p_blindcopyto)>0 Then
maildoc.BlindCopyTo = ListToArray(me.p_blindcopyto)
End If
If me.p_principal”” Then
Call maildoc.ReplaceItemValue(“Principal”, me.p_principal)
‘ If principal is set, we want to fix so mail looks like
‘ it is coming from that address, need to set these fields
Call maildoc.ReplaceItemValue(“From”, me.p_principal)
Call maildoc.ReplaceItemValue(“Sender”, me.p_principal)
Call maildoc.ReplaceItemValue(“ReplyTo”, me.p_principal)
Call maildoc.ReplaceItemValue(“SMTPOriginator”, me.p_principal)
End If

Set bodyChild = me.bodyMIME.CreateChildEntity()
Set header = bodyChild.createHeader(“Content-Type”)
Call header.setHeaderVal(“multipart/mixed”)
Set header = bodyChild.createHeader(“Content-Disposition”)
Call header.SetHeaderVal({attachment; filename=”} & fileName )
’01- 2005.doc should be replaced with the file name of the file you need to attach
Set header = bodyChild.createHeader(“Content-ID”)
‘Call header.setHeaderVal(| fileName |)
’01-2005.doc should be replaced with the file name of the file you need to attach
Set stream = session.CreateStream()

If Not stream.Open(fileName, “binary”) Then Print “Open failed”:
If stream.Bytes = 0 Then Print “File has no content”:

Class NotesMail
Public maildoc As NotesDocument
Private p_replyto As String
Private p_subject As String
Private p_sendto List As String
Private p_copyto List As String
Private p_blindcopyto List As String
Private p_principal As String
Public NoReply As Integer
Public mailbox As NotesDatabase
Public bodyMIME As NotesMIMEEntity
Public header As NotesMIMEHeader
Private session As NotesSession

Public Sub New()
Set session = New NotesSession
Dim mailservername As String
session.Convertmime = False
mailservername = session.Currentdatabase.Server
‘ Using mail.box directly is unsupported, but is the
‘ only way to make the mail look like it is actually
‘ sent from another address, in our case the principal.
Set mailbox = New NotesDatabase(mailservername,”mail.box”)
If mailbox.Isopen = False Then
Print “mail.box on ” & mailservername & ” could not be opened”
Exit Sub
End If
Set me.maildoc = New NotesDocument(mailbox)
Call me.maildoc.ReplaceItemValue(“Form”,”Memo”)
Set me.bodyMIME = me.maildoc.Createmimeentity

‘ *** Send the mail
Public Sub Send()
Dim session As New NotesSession
Dim richStyle As NotesRichTextStyle
If me.subject”” Then
maildoc.Subject = me.subject
End If
If ListItemCount(me.p_sendto)>0 Then
maildoc.SendTo = ListToArray(me.p_sendto)
maildoc.Recipients = ListToArray(me.p_sendto)
End If
If ListItemCount(me.p_copyto)>0 Then
maildoc.CopyTo = ListToArray(me.p_copyto)
End If
If ListItemCount(me.p_blindcopyto)>0 Then
maildoc.BlindCopyTo = ListToArray(me.p_blindcopyto)
End If
If me.p_principal”” Then
Call maildoc.ReplaceItemValue(“Principal”, me.p_principal)
‘ If principal is set, we want to fix so mail looks like
‘ it is coming from that address, need to set these fields
Call maildoc.ReplaceItemValue(“From”, me.p_principal)
Call maildoc.ReplaceItemValue(“Sender”, me.p_principal)
Call maildoc.ReplaceItemValue(“ReplyTo”, me.p_replyto)
Call maildoc.ReplaceItemValue(“SMTPOriginator”, me.p_principal)
End If
‘ If NoReply is set (default), append red warning…
‘If NoReply = True Then
‘Set richStyle = session.CreateRichTextStyle
‘richStyle.NotesFont = 4
‘richStyle.NotesColor = 2
‘richStyle.Bold = True
‘Call me.body.AppendStyle(richStyle)
‘Call me.body.AddNewLine(1)
‘Call me.body.AppendText(“*** DO NOT REPLY TO THE SENDER OF THIS MESSAGE! ***”)
‘Call me.body.AddNewLine(1)
‘Call me.body.AppendText(“*** IT IS AN AUTOMATED SYSTEM MAIL ***”)
‘End If
Call maildoc.ReplaceItemValue(“PostedDate”,Now())
If me.p_principal”” Then
Call maildoc.Save(True,False) ‘ Save in mail.box
Else
Call maildoc.Send(True) ‘ Send mail normally
End If
End Sub

First of all thanks for your help and the Notesmail class which helped me in sending mail from domino.
I need some help of yours in understanding and development of an agent. I am describing to you the exact requirement and my understandings. Please verify this.

Requirement:

Mails are to be send from 2-3 separate id’s to clients. This senders mail ids will be registered mail id’s in domino mail server.
If any mail is bounced back then we need to read the mail and parse it to fetch the reason for this failure.

My Understandings:

All the details will be supplied to the domino server web service from east2 application. As well as the “From” field of the mail.
Then the web service will send mail to the recipients in To, CC and BCC field based on the supplied values and the FROM will be set to the value as supplied.
If there is any failure in email while sending, the failure mail will return to the FROM mail id’s mail box.
The Agent that is running will read this mail from this mail box and send back the details to the web service in the application side.

Process??

I have created a database and in database I have used the Notesmail class to send mail.
I am setting the From field explicitly. Now I need to catch the failure mails through an agent and parse it’s subject line and cause of error.
So as it is in the same database will I get the failure mail’s from here by loading the database or do I need to load some other database.

Open Domino Designer, open the application/database where you want to execute the code, open/create an agent, action button or other element that you want to send the email and put the code there.
I suggest putting the class in a script library (so you can reuse it in different places in that application/database) and reference that script library in yoru code.