Forwarding mail automatically - vba

I receive emails with subjects of the form “### auto fax” where “###” is a variable number of digits. Each of these emails must be forwarded to “####mail2fax.com”. I am looking for ideas on how to automate this.

Your addition “and it needs me to be at office” may be a problem. I am a home user of Outlook. If I want Outlook to do any while I am away, I need to leave my computer switched on. I assume you are an Outlook Exchange user. You can leave instructions that will be obeyed while you are away even if your computer is switched. However, for security reasons, the default is that you cannot leave instructions to forward an email outside your company. As I understand it, you would need to forward the “Auto fax” emails to a colleague. Since we hope to automate the process, this should not be an imposition on your colleague. You would need to create a version of your rule and macro and install it on your colleague’s computer but this should not be too difficult if my reading of this functionality is correct. The point is, that this will not be part of this answer.
A difficulty with VBA is that there are usually several ways of achieving the same effect. This does not matter if you develop your own VBA; pick your favourite way of achieving an effect and experiment until you have a full understanding of that favourite. However, if you ask for help or look for useful snippets of code, you must have a basic familiarity with every way of achieving effects because others will not share your favourite. You may find you have to get the idea of what a snippet does and then rewrite it your way. This code is written using my favourite techniques.
When you write a macro to process emails, you have two issues:
How do you select the emails you wish to process?
How do you process the selected emails?
There are four methods of selecting emails:
The user selects one or more emails and then runs the processing macro.
The macro scans one or more folders looking for emails with particular characteristics and then processes them.
You instruct Outlook to monitor a particular folder and to run a macro every time a new email arrives in that folder.
You set up a rule to select emails as the arrive and link a macro to that rule to process them.
I believe method 4 will be the easiest method to implement your requirement. However, it may not be available. It works fine on my system but apparently those responsible for an Outlook Exchange installation can forbidden it. If method 4 does not work for you, I believe method 3 will be the next best method. However, this answer will use method 1.
I use method 1 whenever I am developing a new email processing macro. If gives me total control of which emails are processed in which order. I can start with simple emails and I can run the macro against the same email again and again until I get the macro working just the way I want. Once I am satisfied with the macro, I can switch to whichever of the other methods is most appropriate.
This is the first version of my processing macro:
Public Sub ForwardAndMoveEmail(ByRef ItemCrnt As Object)
Dim FaxNum As String
Dim ItemNew As MailItem
Dim Subject As String
If ItemCrnt.Class <> olMail Then
' Ignore item if it is not an email
Exit Sub
End If
Subject = ItemCrnt.Subject
If LCase(Right$(Subject, 9)) = " auto fax" Then
‘ Only process email if the subject ends with case-insensitive " auto fax"
FaxNum = Mid$(Subject, 1, Len(Subject) - 9)
With ItemCrnt
Subject = "Fax from " & .Sender & " (" & .SenderEmailAddress & ")"
Set ItemNew = .Forward
End With
With ItemNew
.Subject = Subject
' Clear existing recipient(s)
Do While .Recipients.Count > 0
.Recipients.Remove (1)
Loop
.Recipients.Add FaxNum & "#mail2fax.com"
.Save
End With
End If
ItemCrnt.Move ItemCrnt.Parent.Parent.Folders("Faxed")
End Sub
The item to be processed is a parameter to this macro. Note that I have typed it as an Object rather than as a MailItem. Then note that the first statements of the macro checks that the item is a MailItem (Class = olMail). With method 1, the user could select something other than a MailItem. This check ensures this user error causes no problem for the macro.
Next the macro checks the Subject ends in “ auto fax” or “ Auto fax” or “ AUTO FAX” or any other variation. With method 1, the user could select the wrong MailItem. With method 3, every email is passed to the macro. Hence, the check that it is a macro to be forwarded to the fax service.
If I decided in advance which selection method I was going to use, I would not need to perform all these checks. I think that being able to change the selection method is worth the extra checks.
The macro extracts the leading characters of the Subject. I do not check that they are numeric although such a check could be added if it was important.
The macro creates a new Subject for the forwarded email. I do not know if you would want a new Subject but this demonstrates what you can do if it was helpful.
Set ItemNew = .Forward creates the item to be forwarded. Note that this statement is within a With block. This is the same as Set ItemNew = ItemCrnt.Forward.
The macro then works on ItemNew. It changes the Subject, it clears the existing recipients and adds the new one and then saves the new email as a draft.
The last statement of the macro is something else you did not ask for but which may be useful. I have created a folder named “Faxed” and I move the original email to it. This saves the original email without cluttering your Inbox.
Consider ItemCrnt.Parent.Parent.Folders("Faxed"). This is the folder to which the item is to be moved. I have chained properties together in a way that probably looks strange but is straightforward once you understand it.
ItemCrnt is the original mail item.
ItemCrnt.Parent is a property of ItemCrnt. Many objects have parents. For a MailItem it is the folder holding the MailItem; that is, folder “Inbox”.
`ItemCrnt.Parent.Parent is a property of folder “Inbox”. For a folder, its parent is the folder containing it. Since folder “Inbox” is a top-level folder, its parent is the store holding it. A “store” is a file in which Outlook stores folders, mail items, calendar items, tasks and many other things.
Having gone all the way up to the store, ItemCrnt.Parent.Parent.Folders("Faxed") goes down to a folder within the store.
The macro that calls ForwardAndMoveEmail is:
Option Explicit
Sub SelectEmailsUser()
Dim Exp As Explorer
Dim ItemCrnt As Object
Dim MailItemCrnt As Object
Set Exp = Outlook.Application.ActiveExplorer
If Exp.Selection.Count = 0 Then
Call MsgBox("Please select one or more emails then try again", vbOKOnly)
Exit Sub
End If
For Each ItemCrnt In Exp.Selection
If ItemCrnt.Class = olMail Then
Call ForwardAndMoveEmail(ItemCrnt)
End If
Next
End Sub
Don’t worry too much about this macro at this stage. Study it later when you are ready to develop your next processing email. It is a macro I wrote a long time ago. Each time I want to test a new email processing macro I simply change statement Call ForwardAndMoveEmail(ItemCrnt) to call the new macro.
This is not the final version of the processing macro although it is probably the final version of this evening. Please:
Copy the two macros to an Outlook module. The two macros can be in either order but Option Explicit must be at the top of the module.
Create folder “Faxed”. At the same level as folder “Inbox”.
Select one or more of the “Auto fax” emails and run macro SelectEmailsUser.
Check the processed “Auto fax” emails are now in folder “Faxed”.
Review the emails in folder Drafts. I think these emails are unsatisfactory. I will tell why later and what I think you should do to make them satisfactory.
Part 2
I ended the first part of this answer by saying I did not think the draft email my macro had created was satisfactory.
The problem for me is that the original email is headed up by a typical “forwarded” header: original sender, my name, date sent and subject. The author of the email has presumably spent some time creating the text of the email and would not want this irrelevant header prefixing their text. So how was I to stop this header being included in the email sent to “mail2fax.com”?
My first idea was to use method “Copy” instead of method “Forward”. This did give a satisfactory appearance but was slight awkward. With the statement Set ItemNew = ItemCrnt.Forward, ItemNew is a draft email ready to be finished and saved in folder “Drafts” or sent. But with the statement Set ItemNew = ItemCrnt.Copy, ItemNew is a received email and, when saved, is placed in folder “Inbox”. I have sent a copied email and the appearance when it arrives at one of my secondary email addresses looks satisfactory.
My second idea needs an introduction. An email can have three bodies: plain text, HTML or RTF. I have never seen an email containing a RTF (Rich Text Format) body. RTF was probably a sensible format 20 years ago if you wanted more that plain text. But today, HTML is so powerful that the same email can be rearranged for a large PC screen, a tablet or a smart phone so it is always easy to read. So for all practical purposes there are only two formats for an email: plain text and HTML. If an email has both plain text and HTML bodies, it is the HTML body that is shown to the reader. The VBA programmer can look at either or both bodies but the reader is not told that there is a plain text body. Very occasionally, I have seen a carefully constructed plain text body that has been designed for an email package that cannot handle HTML. But normally the plain text body is just the HTML body with the HTML tags stripped out.
My second idea was to use method “Forward” but then to copy the text and HTML bodies from the original email.
In the above code, you will find:
.Subject = Subject
' Clear existing recipient(s)
Please replace these two lines with:
.Subject = Subject
.Body = ItemCrnt.Body
.HtmlBody = ItemCrnt.HtmlBody
' Clear existing recipients
With this change, the draft emails will not have a “forwarded” header.
I assume you know who these emails are being faxed to. I suggest you contact one or two and say you are going to conduct an experiment. They will have already received faxes as a result of you using the keyboard interface to forward these emails. Send some of the draft emails created by my macro and ask for the recipients’ opinion on the new appearance. If they prefer the new appearance, we will be ready to move on to stage 3: Automating these emails. If they do not like the new appearance, you will need to ask them what is wrong about the new appearance and we will have to attempt to fix the problem.
Part 3
Please do not follow the instructions in this part until you are convinced that emails created by macro ForwardAndMoveEmail are as they should be. This part is about automating the process so the emails will be sent without you having any opportunity to check or correct them before they are sent.
Please make the following changes to macro ForwardAndMoveEmail:
Replace Public Sub ForwardAndMoveEmail(ByRef ItemCrnt As Object)
by Public Sub ForwardAndMoveEmail(ByRef ItemCrnt As MailItem).
With email selection method 1, it is possible (difficult but possible) to select items that are not MailItemss. I set the type of ItemCrnt to Object so this would not cause an error. To use a macro with a rule, ItemCrnt must be MailItem.
These statements are now redundant:
If ItemCrnt.Class <> olMail Then
' Ignore item if it is not an email
Exit Sub
End If
You can leave these statements since they will do no harm. Alternatively, you could delete them or place a quote in front of each statement.
Replace .Save by .Send.
On my system I can attach a macro to a rule. If you can do the same, it will be the easiest approach. However, some IT departments consider attaching a macro to a rule to be a security risk and disable it. If you find you cannot attach a macro to a rule, you will have to try the event approach. I will add additional instructions if necessary.
The screenshots below are from my home Outlook installation. You have functionality I lack so the screens you see will not be identical. However, my screenshots should be similar enough to yours to be useful.
Select one of these “auto fax” emails. From the “Home” tab click “Rules” then “Create Rule...”. You will get a pop-up window like this:
I created an “Auto fax” email in one of my secondary accounts and sent it to me main account. This is why I am shown as both the sender and the receiver. Because I had selected the “Auto fax” email, its subject is shown. Edit this subject to remove the leading digits. A tick will appear in the box next to the subject to get:
Click “Advanced Options...” to get this pop-up window:
Notice that subject in the line near the top has not been edited. This does not matter; it is the value in the “Step 2” box that matters. Note that if you click “ Auto fax” in the “Step 2” box, you can add extra values. So if some of these emails have slightly different subjects, you can add these alternative values. Click “Next” to get a pop-up window like this:
Near the bottom is “Run a script”. You will have more options and may have to scroll down to see this option. Click the box next to this option. “Run a script” will appear in the “Step 2” box. Click “Run a script” in the “Step 2” box. You will be asked to “Enable macros” if you have not done so already. A new pop-up window will appear showing all the macros that could be selected for this option. I have several possible macros so I will not show you my list. You should only see one macro: ForwardAndMoveEmail. To appear in this list, a macro must be Public and the first parameter must be a MailItem. Select ForwardAndMoveEmail if it isn’t selected and click “OK”. “Run a script” now reads “Run ForwardAndMoveEmail”. Click “Next”. You will get a pop-up window of exceptions which I assume are irrelevant to you. Click “Next” to get the final pop-up:
You can click the box against ‘Run this rule now on messages already in “Inbox”’ to forward any of “Auto fax” emails already received but not forwarded. Click “Finish”.
The “Auto fax” rule is now operational and any “Auto fax” emails will be forwarded automatically. It would be a good idea to monitor folder “Faxed” and check the intended recipients received their faxes.

Related

How to automate saving attachments AND memo writing inside the messages

I want to automate these actions, on an open message (either received or sent):
save all the attachments in a folder through a popup letting the user to select the destination folder
This command already exist inside the Action > Other Actions submenu, but the problem is that no trace remains visible in the message about the former presence of attachments, so - as in Lotus Notes - I would like to:
edit the message to introduce some text at the beginning, something such a message like "Attachment removed on " and, even better, the path where the attachments have been saved.
I tried to start understanding Outlook VBA but I feel rather uncomfortable with it.
You have asked far too much in one question to hope for a complete answer. This site is for programmers to help one another develop. Your question should include some faulty code so a more experienced programmer can explain where you have gone wrong.
I understand you feeling uncomfortable with Outlook VBA. When I started, I bought a highly recommended book and found it very unhelpful. I learnt through experimentation. I still experiment when I want to understand something new. I also read through the Outlook questions and answers here. There are some extraordinarily knowledgeable people answering questions. I find that continually adding to my understanding of what can be achieved with Outlook helpful even if I do not expect to ever use some of the more exotic functionality.
You need to break your complete task into small sub-tasks. If your sub-task is small enough you can probably find something relevant if you search. My breakdown of your task is:
Allow user to identify Outlook folder from which attachments are to be saved.
Allow user to identify disc folder to which attachments are to be saved.
Read down Outlook folder looking for emails with user attachments.
For each email with user attachments:
4(a) save attachments to disc folder,
4(b) delete attachments from email
4(c) edit email body with details of deleted attachments.
I will provide some code to get you started with sub-tasks 1 and 3. You should be able to find help on sub-task 2 without too much difficulty. I will include some guidance on sub-tasks 4(a), 4(b) and 4(c).
I would have thought the easiest way for the user to identify the Outlook folder would be for the user to open that folder and then start the macro. That is the approach I have taken. Please copy this code to a module, open an appropriate Outlook folder and run the macro. I believe I have included enough comments to explain what the macro is doing but ask questions if necessary:
Option Explicit
Sub DemoSelectFolderFromEmail()
Dim Exp As Explorer
' VBA has two types of Folder: Outlook folders and disc folders.
' Since you will need access to disc folders, it is important to be clear
' which type you mean.
Dim FldrSrc As Outlook.Folder
Dim InxA As Long
Dim InxF As Long
Dim ItemCrnt As MailItem
' Get collection of emails selected by user.
Set Exp = Outlook.Application.ActiveExplorer
' Check that an email has been selected.
If Exp.Selection.Count = 0 Then
Call MsgBox("Please select an email then try again", vbOKOnly)
Exit Sub
ElseIf Exp.Selection.Item(1).Class <> olMail Then
Call MsgBox("Please select an email then try again", vbOKOnly)
Exit Sub
End If
' The parent of a selected email is the Outlook folder that contains it.
' In my view, this is the easiest way for the user to identify the source
' folder.
Set FldrSrc = Exp.Selection.Item(1).Parent
Debug.Print FldrSrc.Name
' FldrSrc.Items is a collection of the items in FldrSrc.
' Probably every itemn is a MailItem but I check to be sure.
' ReceivedTime and Subject are just two of the properties that you can access.
' If there are any attachments, I display their names.
' Note that for VBA, signatures and images count as attachments. You will need
' to add code to idetify these attachments if you do not want to save them.
For InxF = FldrSrc.Items.Count To 1 Step -1
With FldrSrc.Items(InxF)
If .Class = olMail Then
Debug.Print .ReceivedTime & " " & .Subject
If .Attachments.Count > 0 Then
For InxA = 1 To .Attachments.Count
With .Attachments(InxA)
Debug.Print " " & InxA & " " & .DisplayName
End With
Next
End If
End If
End With
Next
End Sub
SaveAsFile is the method that writes an attachment to a disc folder. Note that SaveAsFile will overwrite any existing file with the same name. If the same DisplayName is used for different attachments, you will need to have some system of making names unique.
You will need to edit the emails to delete the attachments and then save the edited emails.
An email can have a text body and or an Html body and or a RTF body. I have never seen a RTF body so you can probably ignore them. If an email has both a text and an Html body, the user sees the Html body. It is rare these days for an email not to have an Html body. You can probably get away with just adding a string at the start of the Html body but you will need to experiment to ensure the appearance is satisfactory. I would probably prefer to insert, at the beginning of the body section, a complete Html string controlling background colour, font colour, font size and font name so that I the appearance of the insert text was always the same.

Office 365/Outlook 2016 Move a file with an attachment that contains a string to another folder

I receive multiple log files per day and would like to create a rule or vba script that will move the email to a specified folder. The catch is, it should only be moved if it contains specific text in an xml attachment. I'm new to VBA and couldn't find anything that look particularly helpful online, and I couldn't find a way to do it with a rule.
I am able to find the correct files to move if I do a manual search [ext:xml attachment:TestScriptFailed], but I'm not sure how to translate that into a rule or VBA script to automate the transfer process.
You have been a member for 26 months so you should be aware this site is for programmers to help each other develop. You have asked way too much in a single question and have made no obvious attempt to break it down. If someone gave you macro that was almost what you wanted, would you understand it enough to finish it? I will try to get you started.
I know nothing that suggests a rule exists that can test for a particular string within a particular type of attachment and, if found, save that attachment. I am not an experienced user of rules so this may be my ignorance. The SuperUser site would be a better place to ask about such a rule. I will suggest a macro. Start by running the macro manually every hour or once per day or whenever. There are more advanced techniques but let’s get the macro working before we worry about the most convenient way to run it.
First, look at this answer of mine: How to copy Outlook mail message into excel using VBA or Macros
We get a lot of questions along to lines: “I am trying to extract xxxx from emails and copy it to an Excel workbook”. This is accompanied by an image of the email. What the questioners seem unable to understand is that an image of the email tells us nothing about what the email’s body looks like to a VBA macro. Is it text or Html or both? If Html, is the formatting native or CSS? Does it use SPAN or DIV elements with class or id attributes to identify the different sections?
The referenced macro was an attempt to help questioners understand this issue. It creates a new Excel workbook and outputs to it the major properties of every email in Inbox.
There is nothing in your question to suggest you are interested in output to Excel but I think this is a good start for you. It reads down Inbox examining every email. It extracts subject and sender which might be interesting. It lists the type and name of every attachment which you will need. It outputs the text and Html bodies which might be interesting.
Download that macro, change the destination folder as instructed and run the macro. Search the workbook for one of your “log file” emails. Is the text within the Xml file the only indication that it is a log file email? This macro gives the structure you want (it reads down the Inbox) but contains lots of stuff of no interest to you. You can either delete the uninteresting bits from that macro or create a new macro by extracting the interesting bits. Can you do that? If you cannot, you will not be able to cope with the more advanced functionality necessary for a complete solution to your requirement.
I will have to update, the referenced answer. I have recently upgraded to Outlook 2016 and have found an issue. My installation does not use the default Inbox which the macro searches so the macro would create an empty workbook. Outlook 2016 has created a “store” per email address with names of the form: abcdefghi#isp.com. In the folder pane, these are the top names in each hierarchy. Each of these stores contains its own Inbox which is where new emails sent to the relevant address are stored. If your installation is like mine, you will have to replace:
Set FolderTgt = CreateObject("Outlook.Application"). _
GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
by
Set FolderTgt = CreateObject("Outlook.Application"). _
GetNamespace("MAPI").Folders("abcdefghi#isp.com").Folders("Inbox")
Once you have the structure of your macro, the next problem is to identify the emails with an Xml attachment that contains the identifying text. You cannot look at an email’s attachment directly. You have to save them to disc and process them there. With VBA you can open an Xml file as a text file and scan for the identifying text. If I understand correctly, it is Xml files containing the identifying text you require. If so, if an Xml contains the identifying text, it is left on disc otherwise it is deleted. If the Xml file is retained, you need to move the email to another folder so it will not be examined again.
I have: (1) saved attachments to disc, (2) moved emails from one folder to another and (3) processed text files with VBA, although never from Outlook, but never in one macro. I will treat this as a training exercise for myself and develop the code you need to drop into the macro I have told you to develop.
Possible issue 1: How big are these log files? There seem to be a limit of around 15Mb for emails. VBA can easily process files of 15Mb but you do not want to load an entire file of this size into memory if the identifying text is in the first 1,000 bytes.
Possible issue 2: Do the log files have unique names? If they have unique names, they can be saved under those names. If they do not have unique names, unique names will have to be generated for them. A unique name could be as simple as “LFnnnn.Xml” where “nnnn” is one more than the number of the previous log file. Alternatively, it could be as complex as you want.
Update
Rereading your question, I believe if I may have misinterpreted your requirement. I read that you wanted the log file attachments moved to a disc folder. I believe niton read it the same way. I now believe you want the mail item moved to a new Outlook folder and do not specify what is to happen to the log file attachment. I do not think this misinterpretation is important or makes a material difference to the required macro. An email containing a log file has to be moved to a new Outlook because otherwise it would be processed again and again. A log file has to be extracted to a disc folder so that its contents can be checked. My code leaves an Xml file containing the identifying text on disc. One additional statement would delete such an Xml file just as those Xml files that do not contain the identifying text are deleted. I assume the log files have to be extracted sometime. Perhaps you did not appreciate that they would have to be extracted to meet your requirement. I leave you to decide whether or not to add that Kill statement.
I said the default Inbox may not be the Inbox into which these emails are loaded. I have created a little macro that outputs the user name of the store containing the default Inbox which you may find helpful:
Sub DsplUsernameOfDefaultStore()
Dim NS As Outlook.NameSpace
Dim DefaultInboxFldr As MAPIFolder
Set NS = CreateObject("Outlook.Application").GetNamespace("MAPI")
Set DefaultInboxFldr = NS.GetDefaultFolder(olFolderInbox)
Debug.Print DefaultInboxFldr.Parent.Name
End Sub
The following macro does all the heavy lifting for your requirement:
Public Sub SaveInterestingAttachment(ByRef ItemCrnt As MailItem, _
ByVal IdentExtn As String, _
ByVal IdentText As String, _
ByVal DestDiscFldr As String, _
ByRef DestOlkFldr As MAPIFolder)
' * ItemCrnt may contain one or more attachments which have extension
' IdentExtn and which contains text IdentText. If it contains such
' attachment(s) then the macro:
' * saves all such attachments to disc folder DestDiscFldr
' * moves the mail item to output folder DestOlkFldr.
' * Comparisons of IdentExtn and IdentText against file extensions and
' contents are case insensitive because the strings are converted to
' lower case before comparisons.
' * The phrase "saves all such attachments" is perhaps slightly
' misleading. An attachment can only be checked to contain the
' identifying text by saving it to disc, opening it and scanning the
' contents. So all attachments with extension IdentExtn are saved to
' disc and those that do not contain IdentText are deleted.
' Warning: This code assumes DestDiscFldr has a trailing \
' Warning: This code does not test for an existing file with the same name
' Warning: To compile, this macro needs a Reference to "Microsoft Scripting
' RunTime". Click Tools then References. Click box against
' "Microsoft Scripting RunTime" if not already ticked. The Reference
' will be at the top if ticked. Unticked references are in
' alphabetic sequence.
Const ForReading As Long = 1
Const OpenAsAscii As Long = 0
Dim FileContents As String
Dim FileXml As TextStream
Dim Fso As FileSystemObject
Dim InxA As Long
Dim LcExtn As String: LcExtn = LCase(IdentExtn)
Dim LenExtn As Long: LenExtn = Len(IdentExtn)
Dim LcIdText As String: LcIdText = LCase(IdentText)
Dim MoveEmail As Boolean
Dim PathFileName As String
With ItemCrnt
If .Attachments.Count > 0 Then
Set Fso = CreateObject("Scripting.FileSystemObject")
MoveEmail = False
For InxA = 1 To .Attachments.Count
If Right$(LCase(.Attachments(InxA).FileName), 1 + LenExtn) = _
"." & LcExtn Then
' My test files do not have unique names. Adding received time and
' subject was an easy way of making the names unique and demonstrates
' some options.
PathFileName = DestDiscFldr & Format(.ReceivedTime, "yymmddhhmmss") & _
" " & .Subject & " " & _
.Attachments(InxA).FileName
.Attachments(InxA).SaveAsFile PathFileName
Set FileXml = Fso.OpenTextFile(PathFileName, ForReading, OpenAsAscii)
FileContents = FileXml.ReadAll
' If your log files are large snd the identifying text is near
' the beginning, Read(N) would read the first N characters
If InStr(1, LCase(FileContents), LcIdText) <> 0 Then
' Xml file contains identifiying text
' Leave Xml on disc. Move email to save folder
MoveEmail = True
FileXml.Close
Else
' Delete Xml file. Leave email in Inbox unless another attachment
' contained the identifying text
FileXml.Close
Kill PathFileName
End If
Set FileXml = Nothing
End If
Next
If MoveEmail Then
.Move DestOlkFldr
End If
Set Fso = Nothing
End If
End With
End Sub
This macro has five parameters:
A reference to the Mail Item to be tested.
The value of the extension to be tested.
The value of the identifying text.
The value of the disc folder to which attachments are to be saved.
A reference to the Outlook folder to which appropriate Mail Items are to be moved.
I am very confident that eventually this code will have to be called from two different parent macros so making the Mail Item a parameter was necessary. The other parameters could have been hard coded into the macro but making them parameters was no extra effort and parameters are usually easier to explain that values buried in the body of a macro.
You need to work down this macro reading the comments and reviewing the statements. My test data is based on my understanding of your requirement. If I have misunderstood and my test data is faulty, this macro may fail with your data. You will need to carefully check the code and then carefully test it with your data.
I needed a test harness to test this macro since a macro with parameters cannot be called by the user. If you have created a macro to read down the Inbox, it will be very similar to my test harness. My test harness reads down the Inbox and calls SaveInterestingAttachment for each Mail Item.
Even more than SaveInterestingAttachment, this macro must be carefully checked and updated. This macro references folders on my disc and folders within my Outlook installation. These references will have to be updated.
Sub TestSaveInterestingAttachment()
' For every mail item in Inbox, call SaveInterestingAttachment.
Dim DestOlkFldr As MAPIFolder
Dim SrcOlkFldr As MAPIFolder
Dim InxItemCrnt As Long
Dim NS As Outlook.NameSpace
Set NS = CreateObject("Outlook.Application").GetNamespace("MAPI")
' You only need one of the next two Set statements. If your Inbox is not
' Outlook's default then amend the second to reference your default Inbox.
' This is the easiest way to reference the default Inbox.
' However, you must be careful if, like me, you have multiple email addresses
' each with their own Inbox. The default Inbox may not be where you think it is.
Set SrcOlkFldr = NS.GetDefaultFolder(olFolderInbox)
' This references the Inbox in a specific PST or OST file.
' "abcdefghi#MyIsp.com" is the user name that Outlook gave the PST file in
' which it stores emails sent to this account when I created the account. The user
' name is the name Output displays to the user. The file name on disk is different.
Set SrcOlkFldr = NS.Folders("abcdefghi#MyIsp.com").Folders("Inbox")
' I do not know where you want to save processed emails.
' In this description, a "store" is a file on disc in which Outlook stores
' your mail items, calendar items, tasks and so on. When you look at the
' folder pane, names against the left edge are the user names of stores.
' Indented names are folders within a store. The name of the file on disc
' is probably the same as the user name but with an extension of PST or OST.
' The first Set statement below shows how to reference a folder at the same
' level as Inbox in the same store. It does this by using property Parent to
' go up one level and then property Folders to go down one level.
' The second Set statement below shows how to reference a sub-folder of
' Inbox. It does this by using property Folders to go down one level.
' The third Set statement below shows how tp reference a folder "Processed2"
' within folder "Inbox" within store "outlook data file".
' None of these Set statements will meet your requirements. Use these
' examples to build a Set statement suitable for your requirements.
Set DestOlkFldr = SrcOlkFldr.Parent.Folders("!Tony")
Set DestOlkFldr = SrcOlkFldr.Folders("Processed3")
Set DestOlkFldr = NS.Folders("outlook data file").Folders("Inbox").Folders("Processed2")
' This examines the emails in reverse order.
' If I process email number 5 and then move it to another folder,
' the number of all subsequence emails is decreased by 1. If I looked at the
' emails in ascending sequence, email 6 would be ignored because it would have
' been renumbered when I looked for it. By looking at the emails in reverse
' sequence, I ensure email 6 has bee processed before the removal of email 5
' changes its number.
With SrcOlkFldr.Items
For InxItemCrnt = .Count To 1 Step -1
If .Item(InxItemCrnt).Class = olMail Then
' I am only interested in mail items.
' You will need to replace the identying text and the
' destination disc folder
Call SaveInterestingAttachment(.Item(InxItemCrnt), "Xml", _
"identifying text", _
"C:\DataArea\SO\", DestOlkFldr)
End If ' .Class = olMail
Next InxItemCrnt
End With
End Sub
I have attempted a second test harness. I have recently upgraded to Outlook 2016 and this is the first time I have attempted to use events with it. Code which worked perfectly with my previous version no longer works. There are a number of possible reasons for this code not working. Until I have identified the cause, I will give no further information about this second test harness.
Update 2
I have now fixed the problem with my second test harness. A statement that worked with Outlook 2003, which I was still using until a couple of months ago, apparently does not work with Outlook 2016.
You will need a routine based on my first test harness because that routine searches Inbox for log file emails that have already arrived. I also believe it is an easier routine for testing SaveInterestingAttachment until you have updated it to your exact requirements.
The second test harness sits in the background monitoring new emails and processing those containing log files.
I have a home installation and emails register as new when they are downloaded from my ISP’s server to my hard drive. An email can only be downloaded while I have Outlook open. Once I have run test harness 1 to clear my Inbox of previously received log file emails, I can rely on test harness 2 to handle any future log file emails.
If you have an office installation, then your emails may register as new when they reach your organisation’s server. If that is the case, you will always need a routine based on test harness 1 to handle those log file emails that arrive overnight or whenever you do not have Outlook open.
From within Outlook’s Visual Basic Editor, look as the Project Explorer pane. On my installation, the top line is “Project1 (VbaProject.OTM)”. On your installation, the top line might be slightly different.
If there is a “+” to the left of “Project1 (VbaProject.OTM)”, click that “+” to display the items under “Project1 (VbaProject.OTM)”. On my installation these are: “Microsoft Outlook Objects”, “Forms” and “Modules”. You will not have any forms.
If there is a “+” to the left of “Microsoft Outlook Objects”, click that “+” to display the items under “Microsoft Outlook Objects”. The only item displayed will be “ThisOutlookSession”.
Click “ThisOutlookSession” and the code area will become blank. This is a special code area. Previously you will have created modules which are suitable for storing general routines. The code below will only work if it is within “ThisOutlookSession”.
As before, this code will have to be amended to match your Outlook installation and your disc layout. The full code is at the bottom but I introduce it bit by bit to help you understand what it is doing.
My code contains:
Option Explicit
Two variables that can be accessed by either of the subroutines.
Subroutine Application_Startup()
Subroutine InboxItems_ItemAdd(ByVal Item As Object)
You should have Option Explicit at the top of every module. Look it up if you do not know why.
Subroutine Application_Startup() will be executed every time you open Outlook. With this routine in place, you will be warned about “ThisOutlookSession” before Outlook opens. You need to enable macros if Application_Startup() is to be executed.
I suggest you start by copying the following:
Private Sub Application_Startup()
' This event routine is called when Outlook is started
Dim UserName As String
With Session
UserName = .CurrentUser
End With
MsgBox "Welcome " & UserName
End Sub
Having copied this code to "ThisOutlookSession", close Outlook and save your VBA project. Reopen Outlook, enable macros and you will see a message box saying "Welcome Stephanie". This serves no useful purpose but ensures we have the envelope correct before we do anything important.
Copy: Private WithEvents InboxItems As Items. Study the statement starting Set InboxItems = and the comments above it. You will need to construct a version of this statement appropriate for your Inbox. This Set statement makes InBoxItems reference to the Inbox. To confirm, go to the end of the macro where you will find:
Debug.Print InboxItems.Count
If InboxItems.Count > 0 Then
With InboxItems.Item(1)
Debug.Print .ReceivedTime & " " & .Subject & " " & .SenderEmailAddress
End With
End If
These statements output the number of items in the Inbox and details of the first email which is almost certainly the oldest email. Once you have copied these statements, close Outlook, save the VBA project and then open Outlook again. If all is as it should be, the Immediate Window will contain a count and details of an email. If it is not, we need to identify the cause and correct it before continuing.
Copy: Private DestOlkFldr As MAPIFolder. Study the statement starting Set DestOlkFldr = and the comments above it. You will need to construct a version of this statement appropriate for your destination Outlook folder. Again go to the end of the macro where you will find:
Debug.Print DestOlkFldr.Name
Debug.Print DestOlkFldr.Parent.Name
Debug.Print DestOlkFldr.Parent.Parent.Name
On my system these display:
Processed2
Inbox
Outlook Data File
Copy or create as many Debug.Print statements as appropriate for how deeply nested your destination Outlook folder is. Close Outlook, save the VBA project and then open Outlook again. Are the correct names displayed? If so, Sub Application_Startup() is correct. Delete the diagnostic statements which are no longer required.
We are now ready to create Sub InboxItems_ItemAdd(ByVal Item As Object). I would start with:
Private Sub InboxItems_ItemAdd(ByVal Item As Object)
If TypeOf Item Is MailItem Then
With Item
Debug.Print "Mail item received at " & .ReceivedTime & " from " & _
.SenderEmailAddress & "(" & .Sender & ")"
End With
End If
End Sub
Close Outlook, save the VBA project, open Outlook again and wait for some emails to arrive. If necessary, send yourself an email. Details of those emails should be in the Immediate Window.
Finally, update and copy this statement:
Call SaveInterestingAttachment(Item, "Xml", _
"identifying text", _
"C:\DataArea\SO\", DestOlkFldr)
Close Outlook, save the VBA project, open Outlook again and wait for some log file emails to arrive. Are they being processed correctly?
Finally, a recap:
Application_Startup() is a reserved name. A subroutine with this name will be executed automatically when Outlook is opened. This is an example of an event routine. Event routines are executed when the appropriate event occurs. I have included the code in Application_Startup()necessary to prepare for the new email arrived event.
InboxItems_ItemAdd(ByVal Item As Object) is the reserved name and mandatory specification for the Add item to InboxItems (that is new email arrived) event routine. InboxItems was the WithEvents variable we declared at the top and initialised with Application_Startup().
If you are not used to thinking about computer events and what you want to happen when they occur, they can be a little tricky to understand although once you do, you will have difficulty remembering what the problem was. I have introduced them in tiny steps. This is how I try out new functionality. If necessary, sleep on it. Trust me, suddenly it will all make sense.
Come back with questions as necessary but the more you can understand on your own, the faster you will develop.
Option Explicit
Private WithEvents InboxItems As Items
Private DestOlkFldr As MAPIFolder
Private Sub Application_Startup()
' This event routine is called when Outlook is started
Dim UserName As String
With Session
' In TestSaveInterestingAttachment() you have a statement like:
' Set SrcOlkFldr = NS.GetDefaultFolder(olFolderInbox)
' or Set SrcOlkFldr = NS.Folders("abcdefghi#Isp.com").Folders("Inbox")
' You need a similar statement here without the "NS" at the beginning
' and with ".Items" at the end. For example:
'Set InboxItems = .GetDefaultFolder(olFolderInbox).Items
Set InboxItems = .Folders("abcdefghi#Isp.com").Folders("Inbox").Items
' In TestSaveInterestingAttachment() you have a statement like:
' Set DestOlkFldr = SrcOlkFldr.Parent.Folders("!Tony")
' or Set DestOlkFldr = SrcOlkFldr.Folders("Processed3")
' or Set DestOlkFldr = NS.Folders("outlook data file").Folders("Inbox").Folders("Processed2")
' There is no equivalent of SrcOlkFldr here so you cannot use the first two formats
' as a basis for the statement here. You must use the third format, without the
' leading NS, at the basis for the statement here. For example:
Set DestOlkFldr = .Folders("outlook data file").Folders("Inbox").Folders("Processed2")
UserName = .CurrentUser
End With
MsgBox "Welcome " & UserName
Debug.Print InboxItems.Count
If InboxItems.Count > 0 Then
With InboxItems.Item(1)
Debug.Print .ReceivedTime & " " & .Subject & " " & .SenderEmailAddress
End With
End If
Debug.Print DestOlkFldr.Name
Debug.Print DestOlkFldr.Parent.Name
Debug.Print DestOlkFldr.Parent.Parent.Name
End Sub
Private Sub InboxItems_ItemAdd(ByVal Item As Object)
' This event routine is called each time an item is added to Inbox because of:
' "Private WithEvents InboxItems As Items" at the top of this ThisOutlookSession
' and
' "Set InboxItems = Session.GetDefaultFolder(olFolderInbox).Items"
' or "Set InboxItems = Session.Folders("abcdefghi#Isp ").Folders("Inbox").Items"
' within "Private Sub Application_Startup()"
If TypeOf Item Is MailItem Then
With Item
Debug.Print "Mail item received at " & .ReceivedTime & " from " & _
.SenderEmailAddress & "(" & .Sender & ")"
End With
' You will need to replace the identying text and the
' destination disc folder
Call SaveInterestingAttachment(Item, "Xml", _
"identifying text", _
"C:\DataArea\SO\", DestOlkFldr)
End If
End Sub

How to identify the item when using a script

I have generated a rule which then actions a SCRIPT.
I want to get details from the identified email message - i.e. the body then output to a file that can be read elsewhere.
My logic sort of works but I am currently retrieving the contents of the selected or highlighted email rather than the email identified by the rule.
INFO ONLY
[Specifically the email is a FOREX Signal with a defined Subject. I then want to get the contents of this email to a file that can subsequently be read by an Expert Advisor running on a MetaTrader 4 platform]
struggled to get meaningful tags accepted - i.e. script or OutlookRules
You pass the mailitem as a parameter.
If, for example, you pass Item then process Item.
Sub CustomMailMessageRule(Item As Outlook.MailItem)
MsgBox "Mail message arrived: " & Item.Subject
End Sub

Turn on envelope icon for new mail for specific mailbox account only

I have two mailbox accounts in Outlook 2010.
Primary: Bob#something.com
Secondary: help#something.com
Every time I receive new mail to help#something.com I get the envelope of new mail.
I want to receive the envelope only for Bob#something.com
I cancelled the option to receive envelopes in Outlook.
I created a rule in Outlook 2010 under the account of Bob#something.com
I have the option to run a script but its empty.
I need to write VBA code that can do it:
If (mail was send to Bob#something.com) Then
show Envelope
End If
I know that I can just add the folder of help#something.com instead of its account but it is not possible in my environment (cloud users).
I searched on the web and didn't find anything like this. For example, I did not find what code line can turn the envelope on in Windows.
If you find displaying a message box is an acceptable alternative then the Run a Script format is described here Outlook's Rules and Alerts: Run a Script
From the rule you pass the new mail as "Item" like this:
Public Sub DisplayMessageBox(Item As Outlook.MailItem)
' You need not do anything with "Item" just use it as a trigger
MsgBox "New mail in Bob#something.com"
' or you can enhance the message using "Item" properties
MsgBox "New mail in Bob#something.com - Subject: " & Item.Subject
End Sub
You may also consider ItemAdd or NewMailEx which are a little more complex.
There is an ItemAdd example here How do I trigger a macro to run after a new mail is received in Outlook?

Macro to save e-mail as text file, to be used in a rule

My problem is very similar to this thread and this one. I think my issue is to combine these two questions.
I am running:
OS: Windows 7 Enterprise Professional
Outlook 2010
VBA version 7.0
By reading these two questions as well as some other pages from Microsoft and elsewhere, I was able to open the VB editor and paste into it, this simple code:
Sub SaveEmail(msg As Outlook.MailItem)
' save as text
msg.SaveAs "C:\Users\mel\mailsave\email.txt" & Format(Now, "YYYYMMDDHHMMSS"), _
olTXT
End Sub
Is the "format" portion of my msg.SaveAs line, going to save a unique text file for each email matching my rule?
How do I run this macro to test and if successful, how do I run it repeatedly?
I tried going to the run menu and selecting run "sub/user form" item but the next dialog box is asking what to run and does not populate a list of macros available for running. Clicked on "save" icon but nothing changed.
Specifying a method with that signature (Sub method (var As Outlook.MailItem)) allows you to use the method when creating a mailbox rule. As far as I understand your question, you're beyond that point.
Question 1
The format portion of your code is only going to save a unique file at most once per second. You're appending the current date and time to the file. Your main problem, however, is not the timestamp, but the file format. You should apply the timestamp before the file extension, e.g.
msg.SaveAs "C:\Users\mel\mailsave\email" & Format(Now, "YYYYMMDDHHMMSS") & ".txt", olTXT
Question 2
If you add the macro to a rule, it will be run when the rule is matched. The macro can be tested by creating a method that grabs the currently selected mail, e.g.
Sub TestSaveEmail()
Call SaveEmail(ActiveExplorer.Selection(1))
End Sub
This macro can then be run by setting the cursor within the method and pressing F5.
The macro can also be added to the Outlook user interface by customizing the ribbon and adding a macro button. For help on customizing the ribbon, refer to the following article:
Customize the ribbon