Script to change email addresses of outlook contacts - scripting

Exchange 2007 environment. Every mailbox has their own local copy of contacts.
I just changed our domain in order to shorten it, and instead of having users go in and change each one of their local contacts, I'd like to write a script that iterates through each of their local contacts, and if the email address ends with '#oldemailaddress.com' I'd like to change it to '#newaddy.com'.
It would also be nice if the same script would open their .n2k file and edit those addresses as well.
Is this possible? Would it be a script for each client or would it be a script or cmd I could run on my Exchange 2007 server (sans the n2k)?
Thanks!

I think I found the solution on my own.
Const olFolderContacts = 10
sOldDomain = "#olddomain.com"
sNewDomain = "#newdomain.com"
Set oOutlook = CreateObject("Outlook.Application")
Set oNamespace = oOutlook.GetNamespace("MAPI")
Set oContactFolder = oNamespace.GetDefaultFolder(olFolderContacts)
For Each item in oContactFolder.Items
If InStr(1, item.Email1Address, sOldDomain, vbTextCompare) > 0 Then _
item.Email1Address = Replace(item.Email1Address, sOldDomain, sNewDomain, 1, 1, vbTextCompare)
If InStr(1, item.Email2Address, sOldDomain, vbTextCompare) > 0 Then _
item.Email2Address = Replace(item.Email2Address, sOldDomain, sNewDomain, 1, 1, vbTextCompare)
If InStr(1, item.Email3Address, sOldDomain, vbTextCompare) > 0 Then _
item.Email3Address = Replace(item.Email3Address, sOldDomain, sNewDomain, 1, 1, vbTextCompare)
item.Save
Next
Wscript.Echo "Finished."

Related

Weird characters in email body

I have a little problem with VBScript. There is how it should work. It is a simply code that should go through all emails in particular folder, get particular email body and try to find regular expression. It works correctly on my computer but somehow the same code is not working on other laptop (my friend laptop). Most (not all of them) of emails body look very weird like on attached screen below:
I would like to add that we had the same email messages to test. What is also curious, after use script, it converts first email into these weird characters.
And this is how code looks:
Set objOutlook = CreateObject("Outlook.Application")
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set objMailbox = objNamespace.Folders(Main_mailbox)
Set objMainMailbox = objMailbox.Folders(Main_folder)
Set objFolder = objMainMailbox.Folders(Sub_folder)
Set re = New RegExp
re.IgnoreCase = True
re.Global = True
re.Pattern = "<.+>"
Set colItems = objFolder.Items
NumberOfEmails = colItems.Count
WScript.Echo NumberOfEmails & " emails found"
For i = NumberOfEmails To 1 Step - 1
BodyMsg = colItems(i).Body
Lines = Split(BodyMsg, vbCrlf)
For j = 1 To UBound(Lines)
If InStr(1, Lines(j), "Reply-To:") Then
Set RegMatches = re.Execute(Lines(j))
For Each myMatch In RegMatches
OutputMatch = OutputMatch & " " & myMatch & ";"
OutputMatch = Replace(OutputMatch, "<", "", 1, 1)
OutputMatch = Replace(OutputMatch, ">", "", 1, 1)
EmailCount = EmailCount + 1
Next
End If
Next
Next
I am wondering if it is about encoding or something like that and if that problem is caused by system settings?
If you need some more information that I forgot mention about, please let me know.
That sure sounds like you are running into an NDR (Non-Delivery Report - represented by the ReportItem object) - ReportItem.Body returns gibberish when accessed though the Outlook Object Model. This has been a problem for a few versions of Outlook now.
You can either skip items like this by checking that you only get the MailItem object (Class property must be 43 (olMail)) or use Redemption (I am its author) - its RDOReportItem object does not have this problem.

How to reply to mail based on attachment extension?

I can read code, and adjust code a bit.
I've got access to a company email (say invoice#rr.com).
I want code which looks through all new mail in the inbox of invoice#rr.com (best if this works even when Outlook is not open, but a manually clicked macro would make me happy) and reply to all (with attachment) when:
there is more then one attachment (exception is one .xml and one .pdf file)
the attachment is not .pdf, .xml or .icf
when there is no attachment at all
when the title has the word "reminder"
when the message has the word "reminder"
Besides that, the code needs to move the mail to a subfolder called "send back".
I've been reading forums and one of the problems is a picture in a signature also counts as an attachment.
First try after help from Tony:
Sub reply()
'still need to get rid of all the stuff i dont use below (up to the *) but still not sure about the code so I left it here for now
Dim olInspector As Outlook.Inspector
Dim olDocument As Outlook.DocumentItem
Dim olSelection As Outlook.Selection
Dim olReply As MailItem
Dim olAtt As Attachment
Dim olFileType As String
Dim AttachCount As Long
Dim AttachDtl() As String
Dim ExcelWkBk As Excel.Workbook
Dim FileName As String
Dim FolderTgt As MAPIFolder
Dim HtmlBody As String
Dim InterestingItem As Boolean
Dim InxAttach As Long
Dim InxItemCrnt As Long
Dim PathName As String
Dim ReceivedTime As Date
Dim RowCrnt As Long
Dim SenderEmailAddress As String
Dim SenderName As String
Dim Subject As String
Dim TextBody As String
Dim myDestFolder As Outlook.Folder
'*
Set myDestFolder = Session.Folders("Outlook Data File").Folders("replied")
Set Myselect = Outlook.ActiveExplorer.Selection '(i use this in my test to only process selected test mails)
'Set FolderTgt = Session.Folders("invoice#rr.com").Folders("Inbox") ***(this will replace the code above)
For InxItemCrnt = Myselect.Items.Count To 1 Step -1 '(myselect = foldertgt in live)
With Myselect.Items.Item(InxItemCrnt) '(myselect = foldertgt in live)
'still need a workaround for mail with (1 .PDF and 1 .ICF) or (1 .PDF and 1 .XML)
'those combinations are the only combinations when more then one attachment is allowed
'1st filter
If AttachCount = 0 Then 'no attachment = reply
Reply0
.move myDestFolder
Else
'2nd filter
If AttachCount > 1 Then 'more then one attachment = reply
Reply1
.move myDestFolder
Else
'3rd filter
If InStr(Subject, "Reminder") = 0 Then 'reminders need to go to a different mailbox
Reply2
.move myDestFolder
Else
'4th filter
Select Case olFileType
Case ".pdf, .icf, .xml"
If olFileType = LCase$(Right$(olAtt.FileName, 4)) Then
Exit Sub 'if attachment = pdf or ICF then this sub can exit
Else
Reply3 'all mails with incorrect files
.move myDestFolder
End Select
End If
End If
End If
End If
End With
'replies below
Reply0:
Set olReply = Item.Reply '// Reply if no attachment found
olReply.Body = "this is an automatic generated mail." & vbCrLf & vbCrLf & vbCrLf & vbCrLf & vbCrLf & ".... insert text...." 'still need to insert some text
olReply.Send
Reply1:
Set olReply = Item.Reply '// Reply more then one attachment
olReply.Body = "this is an automatic generated mail." & vbCrLf & vbCrLf & vbCrLf & vbCrLf & vbCrLf & ".... insert text...." 'still need to insert some text
olReply.Send
Reply2:
Set olReply = Item.Reply '// Reply reminders need to go to reminder#rr.com
olReply.Body = "this is an automatic generated mail." & vbCrLf & vbCrLf & vbCrLf & vbCrLf & vbCrLf & ".... insert text...." 'still need to insert some text
olReply.Send
Reply3:
Set olReply = Item.Reply '// Reply not correct file
olReply.Body = "this is an automatic generated mail." & vbCrLf & vbCrLf & vbCrLf & vbCrLf & vbCrLf & ".... insert text...." 'still need to insert some text
olReply.Send
Next
Set olInspector = Nothing
Set olDocument = Nothing
Set olSelection = Nothing
Set olAtt = Nothing
End Sub
2nd try after some more help from Tony: (note: I'm Dutch so some code has dutch words I'll explain them in English behind the code, it basicly is copy paste from his answer, all credits to Tony)
Sub reply()
Dim Fso As New FileSystemObject
Dim DiagFile As TextStream
Dim FldrInvInbox As MAPIFolder
Dim InxA As Long
Dim InxItemCrnt As Long
Dim NumIcfAttach As Long
Dim NumPdfAttach As Long
Dim NumXmlAttach As Long
Dim NumDocAttach As Long
Dim NumDoxAttach As Long
Dim PathDiag As String
Dim Pos As Long
Dim ProcessThisEmail As Boolean
Dim Subject As String
Dim ReminderInBody As Boolean
Dim ReminderInSubject As Boolean
Dim ReminderInBody1 As Boolean
Dim ReminderInSubject1 As Boolean
Set FldrInvInbox = Session.Folders("invoice#rr.com").Folders("Postvak IN") 'Postvak IN = Inbox)
PathDiag = "z:\VBA test" 'location for diagnostics report
Set DiagFile = Fso.CreateTextFile(PathDiag & "\Diag.txt", True, False)
For InxItemCrnt = FldrInvInbox.Items.Count To 1 Step -1
With FldrInvInbox.Items.Item(InxItemCrnt)
' It is unlikely an Inbox will contain anything but emails
' but it does no harm to check
If .Class = olMail Then
' Extract information that will identify if this email is to be processed
ProcessThisEmail = True ' Assume True until find otherwise
'Below i'm looking for reminder, payment reminder and other similiar text in subject, dutch words are betalingsherinnering and openstaande posten
If InStr(1, LCase(.Subject), "betalingsherinnering") = 0 Then
ReminderInSubject = False
Else
ReminderInSubject = True
ProcessThisEmail = False
End If
If InStr(1, LCase(.Subject), "openstaande posten") = 0 Then
ReminderInSubject1 = False
Else
ReminderInSubject1 = True
ProcessThisEmail = False
End If
'Below i'm looking for reminder, payment reminder and other similiar text in mail, dutch words are betalingsherinnering and openstaande posten
If InStr(1, LCase(.Body), "betalingsherinnering") = 0 Then
ReminderInBody = False
Else
ReminderInBody = True
ProcessThisEmail = False
End If
If InStr(1, LCase(.Body), "openstaande posten") = 0 Then
ReminderInBody1 = False
Else
ReminderInBody1 = True
ProcessThisEmail = False
End If
NumIcfAttach = 0
NumPdfAttach = 0
NumXmlAttach = 0
NumDocAttach = 0
For InxA = 1 To .Attachments.Count
Select Case LCase(Right$(.Attachments(InxA).FileName, "3"))
Case "txt"
NumIcfAttach = NumIcfAttach + 1 'code will be changed soon, need to look at ICF in the name of the attachment
Case "pdf"
NumPdfAttach = NumPdfAttach + 1
Case "doc"
NumDocAttach = NumDocAttach + 1
Case "xml"
NumXmlAttach = NumXmlAttach + 1
End Select
Next InxA
Else ' Not email
ProcessThisEmail = False
End If
End With
' Decide if email is to be processed
If ProcessThisEmail = True Then
If NumXmlAttach > 1 Then
ProcessThisEmail = False
Else
If NumDocAttach <> 0 Then
ProcessThisEmail = False
Else
If NumPdfAttach > 1 Then
ProcessThisEmail = False
Else
If NumIcfAttach > 1 Then
ProcessThisEmail = False
Else
If NumIcfAttach + NumPdfAttach = 2 Then
ProcessThisEmail = True
Else
If NumXmlAttach + NumPdfAttach = 2 Then
ProcessThisEmail = True
Else
If NumXmlAttach = 1 And NumIcfAttach = 0 And NumPdfAttach = 0 And NumDocAttach = 0 Then
ProcessThisEmail = True
Else
If NumPdfAttach = 1 And NumIcfAttach = 0 And NumXmlAttach = 0 And NumDocAttach = 0 Then
ProcessThisEmail = True
Else
If NumIcfAttach = 1 And NumXmlAttach = 0 And NumPdfAttach = 0 And NumDocAttach = 0 Then
ProcessThisEmail = True
Else
If NumXmlAttach + NumPdfAttach + NumIcfAttach = 0 Then
ProcessThisEmail = False
Else
If NumXmlAttach + NumIcfAttach = 2 Then
ProcessThisEmail = False
Else
If NumXmlAttach + NumPdfAttach + NumIcfAttach = 3 Then
ProcessThisEmail = False
Else
If NumIcfAttach + NumPdfAttach <> 2 Then
ProcessThisEmail = False
Else
If NumXmlAttach + NumPdfAttach <> 2 Then
ProcessThisEmail = False
Else
Procisthisemail = False
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
' Output diagnostic information
DiagFile.WriteLine "----- " & InxItemCrnt & " -----"
With FldrInvInbox.Items.Item(InxItemCrnt)
DiagFile.WriteLine "ReceivedTime=" & .ReceivedTime
DiagFile.WriteLine "Sender=" & .Sender
Subject = .Subject
For Pos = Len(Subject) To 1 Step -1
If AscW(Mid(Subject, Pos, 1)) < 1 Or _
AscW(Mid(Subject, Pos, 1)) > 255 Then
Subject = Replace(Subject, Mid(Subject, Pos, 1), "?")
End If
Next
DiagFile.WriteLine "Subject=" & Subject
DiagFile.WriteLine "Reminders: Subject 1=" & ReminderInSubject & _
" Subject 2=" & ReminderInSubject1 & _
" Body 1=" & ReminderInBody & _
" Body 2=" & ReminderInBody1
DiagFile.WriteLine "Attachment counts: ICF=" & NumIcfAttach & _
" PDF=" & NumPdfAttach & " XML=" & NumXmlAttach & _
" Doc=" & NumDocAttach
DiagFile.WriteLine "ProcessThisEmail=" & ProcessThisEmail
End With
' Process email if required
If ProcessThisEmail Then
End If
Next InxItemCrnt
DiagFile.Close
End Sub
My first reaction is: there is way too much untested code in your revised question. I plan to review your code but most of this answer is a tutorial explaining how I would have tackled your requirement.
None of us were born with the knowledge to write the macro you need. I started with Excel VBA which I believe was lucky since the training material for Excel VBA is much better than that for Outlook VBA. I visited a good library and borrowed several “Teach yourself to program Excel” books. I tried them all and then bought the one that was best for my learning style. I would recommend you invest a few days learning Excel VBA. I am confident that this investment will quickly repay itself. I did buy a highly recommended Outlook VBA but was not impressed. The transition from Excel to Outlook was not as easy as it could have been because I have never found a good explanation of the Outlook Object Model. Most of my knowledge is the result of much experimentation over many years. This background means I can usually look at some badly explained Outlook property and be able to deduce what they meant to say.
One of my advantages is that I have spent time studying development and testing theory. There is little in your code that is wrong but I believe a different approach would produce the desired result more quickly.
Another advantage which I would recommend to you is a folder on my system named “Resources” with sub-folders by topic. Every time I complete a development, I look through the code for ideas I might need again. I capture each idea in a file in the appropriate sub-folder with sample code, references to the source and notes on anything I found difficult. I don’t use VBA often enough to remember everything I have learnt. Being able to look for relevant files when starting a new development saves me a lot of time.
That’s enough general points. From your original question, I think you need the following for your requirement:
To read up or down the Inbox of invoice#rr.com.
To identify the number of attachments of an email and, if any, their extensions.
To check if the title of an email includes "remainder".
To check if body of an email includes "remainder".
To reply to selected emails
To move selected emails to folder “send back”
Your original specification of which emails were to be selected is not clear to me. Your added code is a little clearer but adds the complication that you appear to envisage different replies for different emails.
In your code, you do not read up or down the Inbox of invoice#rr.com. Instead you use Inspector to process selected emails. This would allow you to select an email with no attachments, say, and test how you code handles it. I do not think this is a good idea. For me, reading down the inbox and selecting the emails to be processed is the larger and more complicated block of code. I would want to write and test that code before writing the code to replying or moving emails. I would not want to reply to emails until I knew I had everything else correct. I would not want to move emails to a different folder until near the end of testing because I would not want to move them back for a retest.
The potential problem with my approach is the number of emails in the Inbox of invoice#rr.com. How do I test each email is correctly identified as needing a reply or not needing a reply? VBA comes with some diagnostic aids but one of my favourite technique is to write the envelope for my code but to output diagnostic text identifying the decisions made by the code without acting on those decisions. The code I have written shows what I mean by this.
To output diagnostic text, I can use something like Debug.Print "xxxx=" & xxxx where xxxx is a variable. This outputs to the Immediate Window which is often the most convenient technique. But you can only see the last 200 or so displays with Debug.Print and I suspect this will not be enough. Instead I will output to a text file. I do not do this often enough to remember the syntax so I have a file to remind me. File “Output to diagnostic file.txt” contains:
' Needs reference to Microsoft Scripting Runtime
Dim Fso As New FileSystemObject
Dim DiagFile As TextStream
Dim PathDiag As String
PathDiag = ThisWorkbook.Path
PathDiag = CreateObject("WScript.Shell").specialfolders("Desktop")
Set DiagFile = Fso.CreateTextFile(PathDiag & "\Diag.txt", True, False)
DiagFile.WriteLine ""
DiagFile.Close
I do not need the reminder about “Microsoft Scripting Runtime” for Outlook because I referenced it when I first installed Outlook. You will need to open the Outlook VBA Editor, click Tools and select References from the drop down menu. Scroll down the list of libraries and tick “Microsoft Scripting Runtime”. Without this reference, the compiler will not recognise “FileSystemObject” or “TextStream”. I use the first value for PathDiag if I am working with Excel. Here I will use the second value which will create the file on the desktop.
I have taken code from the answer I referenced in my comment. I renamed some of the variables and simplified the handling of attachments. I inserted code from file “Output to diagnostic file.txt”. I added code to extract the values needed for selecting emails. This code only involves simple (for me) statements that I remember how to use correctly. I added the code to output diagnostics to the text file. Here I hit a problem. Execution stopped on DiagFile.WriteLine “Subject=" & .Subject saying this an invalid call. It took me a while to identify the cause and add code to fix it. I will explain this latter. Here is my code:
Option Explicit
Sub ReplyToInvoiceEmails()
Dim Fso As New FileSystemObject
Dim DiagFile As TextStream
Dim FldrInvInbox As MAPIFolder
Dim InxA As Long
Dim InxItemCrnt As Long
Dim NumIcoAttach As Long
Dim NumPdfAttach As Long
Dim NumXmlAttach As Long
Dim PathDiag As String
Dim Pos As Long
Dim ProcessThisEmail As Boolean
Dim Subject As String
Dim ReminderInBody As Boolean
Dim ReminderInSubject As Boolean
Set FldrInvInbox = Session.Folders("tonydallimore23#gmail.com").Folders("Inbox")
PathDiag = CreateObject("WScript.Shell").specialfolders("Desktop")
Set DiagFile = Fso.CreateTextFile(PathDiag & "\Diag.txt", True, False)
For InxItemCrnt = FldrInvInbox.Items.Count To 1 Step -1
With FldrInvInbox.Items.Item(InxItemCrnt)
' It is unlikely an Inbox will contain anything but emails
' but it does no harm to check
If .Class = olMail Then
' Extract information that will identify if this email is to be processed
ProcessThisEmail = True ' Assume True until find otherwise
If InStr(1, LCase(.Subject), "reminder") = 0 Then
ReminderInSubject = False
Else
ReminderInSubject = True
End If
If InStr(1, LCase(.Body), "reminder") = 0 Then
ReminderInBody = False
Else
ReminderInBody = True
End If
NumIcoAttach = 0
NumPdfAttach = 0
NumXmlAttach = 0
For InxA = 1 To .Attachments.Count
Select Case LCase(Right$(.Attachments(InxA).Filename, "3"))
Case "ico"
NumIcoAttach = NumIcoAttach + 1
Case "pdf"
NumPdfAttach = NumPdfAttach + 1
Case "xml"
NumXmlAttach = NumXmlAttach + 1
End Select
Next InxA
Else ' Not email
ProcessThisEmail = False
End If
End With
' Decide if email is to be processed
If ProcessThisEmail Then
If ReminderInSubject Or ReminderInBody Then
ProcessThisEmail = False
ElseIf NumXmlAttach = 1 Then
ProcessThisEmail = False
End If
End If
' Output diagnostic information
DiagFile.WriteLine "----- " & InxItemCrnt & " -----"
With FldrInvInbox.Items.Item(InxItemCrnt)
DiagFile.WriteLine "ReceivedTime=" & .ReceivedTime
DiagFile.WriteLine "Sender=" & .Sender
Subject = .Subject
For Pos = Len(Subject) To 1 Step -1
If AscW(Mid(Subject, Pos, 1)) < 1 Or _
AscW(Mid(Subject, Pos, 1)) > 255 Then
Subject = Replace(Subject, Mid(Subject, Pos, 1), "?")
End If
Next
DiagFile.WriteLine "Subject=" & Subject
DiagFile.WriteLine "Reminders: Subject=" & ReminderInSubject & _
" Body=" & ReminderInBody
DiagFile.WriteLine "Attachment counts: ICO=" & NumIcoAttach & _
" PDF=" & NumPdfAttach & " XML=" & NumXmlAttach
DiagFile.WriteLine "ProcessThisEmail=" & ProcessThisEmail
End With
' Process email if required
If ProcessThisEmail Then
End If
Next InxItemCrnt
DiagFile.Close
End Sub
The first executable statement is Set FldrInvInbox = Session.Folders("tonydallimore23#gmail.com").Folders("Inbox"). You must replace "tonydallimore23#gmail.com" with "invoice#rr.com" or the real name for this store. Apart from this change, this code should run without problems on your system.
Next are the statements to prepare the diagnostic text file and then: For InxItemCrnt = FldrInvInbox.Items.Count To 1 Step -1.
FldrInvInbox.Items is a collection holding all the items in FldrInvInbox. A collection is like an array (if you know what an array is) except you can add items to the middle of a collection and delete items from the middle. FldrInvInbox.Items.Count is the number of items in FldrInvInbox.Items. I am accessing items in this collection by their position. If the code decides item 5 is to be moved to another folder, item 6 become item 5, item 7 becomes item 6 and so on. This would mess up the For-Loop. I am accessing this collection starting from the end. If I move item 1000 to a different folder then items 1 to 999, which I have yet to process, do not move so the For-Loop works properly.
The next block of code extracts properties to variables. I think I have extracted every property you need but you must check. The next block of code decides if an email is to be processed. I like to divide code into blocks like this because it is easier to write and easier to understand if you need to change it in a year’s time. I do not understand how you want to select emails and I am sure my selection code is wrong. You will have to correct this block of code or provide more information on the selection process so I can correct it.
Next is code to create the diagnostic output. On my system, the diagnostic output looks like:
----- 55 -----
ReceivedTime=09/08/2018 13:03:09
Sender=TechTarget Channel Media
Subject=Channel ecosystem sees major shift in partner types
Reminders: Subject=False Body=False
Attachment counts: ICO=0 PDF=0 XML=0
ProcessThisEmail=True
----- 54 -----
ReceivedTime=09/08/2018 11:48:10
Sender=TechTarget
Subject=Industrial control systems a specialised cyber target
Reminders: Subject=False Body=False
Attachment counts: ICO=0 PDF=0 XML=0
ProcessThisEmail=True
----- 53 -----
The first three lines of each email identify the email so you can locate it in the folder. The second three lines are the selection values which I know are wrong.
If I had missed some selection values, you will have to add them. You will have to correct my selection code. You want every “ProcessThisEmail=True/False” line to be correct before we move on to the reply code.
The problem I encountered with the diagnostic code was because of emojis. Execution stopped on the statement outputting the subject for some emails and it took me sometime to locate the cause. The diagnostic file is a simple ASCII text file and an emoji cannot be output to an ASCII text file. I thought of ignoring the problem since you were unlikely to have emojis in your subject. I decided to fix the problem because you have neither the experience to diagnose the problem nor to experience fix it if you did diagnose it. Have a look at my code and try to understand what I have done.
I need you to complete tmy code before we move on to the next section. I will add some text about the next section later but this section is much later than I promised so I will post what I have now.
This next paragraphs are to get you thinking about the email bodies you will create and the ones you have checked for “Reminder”.
An Outlook email can have three bodies: a text body, an Html body and a Rich Text Format (RTF) body. I have never received an email with a RTF body. I have seen a question about them but to my mind they are obsolete; perhaps they were useful before Html became so well known. I will ignore the theoretical existence of RTF bodies. An Outlook email can have a text body, an Html body or both. If there is an Html body, that is the body that is shown to the user. I rarely receive emails without an Html body. The Html bodies I receive vary enormously in both appearance and the Html used to create that appearance. Many are very complicated with style sheets and nested table so the result will look good on a laptop or a smartphone or whatever device the user views it on. I said, an Outlook email can have an Html body without a text body but I cannot find one in my archives for the last few years. I suspect they are being created by Outlook from the Html body by deleting all the Html tags and adding carriage-return-linefeeds to mark deleted paragraphs and table cells.
With an Outlook email, property Body is the text body and property HtmlBody is the Html body. In my code I only check the text body for “Reminder”. This seems sensible since the text body will be a lot smaller and I cannot find an email without a text body. If you want to be ultra-cautious, I will show you how to check the Html body if there is no text body or you might like to consider how to do this as a training exercise.
In your code you have:
olReply.Body = "this is an automatic generated mail." & vbCrLf & vbCrLf & _
vbCrLf & vbCrLf & vbCrLf & ".... insert text...."
You might find that the following gives a more attractive appearance since email packages tend to use a more attractive font when displaying Html bodies if none is specified:
olReply.Body = ""
olReply.HtmlBody = "<HTML><BODY>" & _
"<P>this is an automatic generated mail.</P>" & _
"<P>.... insert text....</P>" & _
"</BODY>" & "</HTML>"
This is very basic Html and is now depreciated but it shows an Html body could be created for little more effort than creating a text body. Html also allows formatting (bold, italic, font size, font colour and so on) which might be helpful.

Counting the number of Excel attachments

I download Excel files that I get e-mailed that meet a given criteria.
If there is no attachment, I need to scrape the body of the e-mail, and paste it into an Excel document.
How do I detect if I've been sent only non-Excel attachments - For example, if someone attaches a PDF.
I figured to count the number of attachments that contain the string ".xls", and if I have more than 0 results, process the downloads. Otherwise, scrape the e-mail body.
I've found:
1) How to count the attachments
oOlItm.Attachments.Count <> 0
2) How to check if any given attachment has a given string in it
(InStr(oOlAtch.FileName, ".xls") > 0)
Something like this will work:
For Each oOlItm In oOlAtch.attachments
If InStr(1, oOlItm.filname, ".xls") > 1 Then cnt = cnt + 1
Next oOlItm
if cnt > 0 then ...
For Each oOlAtch In oOlItm.Attachments
ExcelAttachmentNumber = 0
If (InStr(oOlAtch.FileName, ".xls") > 0) Then
ExcelAttachmentNumber = ExcelAttachmentNumber + 1
Else
End If
Next

How do I select which account I want to create an outlook item for?

I'm writing a script that creates and updates an outlook note. Everything works fine except for one issue.
It creates and updates the note on my personal mailbox and I need it to create and maintain the note on a department mailbox that we all have access to(I have two accounts set up in my Outlook).
It is behaving this way despite the script being triggered by a rule on the department mailbox and being given mail items for the department mailbox. How do I tell VBS/Outlook which notes folder/account I want to use? I cannot find anything anywhere that outlines how to select which account the note will be created under.
I am using Outlook 2016.
Set olkFolder = Session.GetDefaultFolder(olFolderNotes)
Set olkNote = olkFolder.Items.Find("[Subject] = 'Sequential Number'")
If TypeName(olkNote) = "Nothing" Then
Set olkNote = Application.CreateItem(olNoteItem)
olkNote.Body = "Sequential Number" & vbCrLf & "NextValue=" & STARTING_VALUE + 1
GetNextNumber = STARTING_VALUE
Else
arrLines = Split(olkNote.Body, vbCrLf)
For Each varLine In arrLines
If Left(varLine, 10) = "NextValue=" Then
GetNextNumber = CInt(Mid(varLine, 11))
olkNote.Body = "Sequential Number" & vbCrLf & "NextValue=" & GetNextNumber + 1
End If
Next
End If
Instead of using Application.CreateItem, either open the folder from another mailbox using Application.Session.CreateRecipient / Application.Session.GetSharedDefaultFolder and call MAPIFolder.Items.Add or (if the mailbox is already available in Outlook) drill down to that folder starting from Application.Session.Folders and (again) call MAPIFolder.Items.Add.
Replace
Set olkFolder = Session.GetDefaultFolder(olFolderNotes)
...
Set olkNote = Application.CreateItem(olNoteItem)
with
set recip = Session.CreateRecipient("SomeOtherUser#YourDomain.demo")
Set olkFolder = Session.GetSharedDefaultFolder(recip, olFolderNotes)
...
Set olkNote = olkFolder.Items.Add
You can either move the note you created or add directly to the folder in the other account.
From the link, the code to Add:
Sub AddContact()
Dim myNamespace As Outlook.NameSpace
Dim myFolder As Outlook.Folder
Dim myItem As Outlook.ContactItem
Dim myOtherItem As Outlook.ContactItem
Set myNamespace = Application.GetNamespace("MAPI")
Set myFolder = myNamespace.GetDefaultFolder(olFolderContacts)
Set myOtherItem = myFolder.Items("Dan Wilson")
Set myItem = myFolder.Items.Add
myItem.CompanyName = myOtherItem.CompanyName
myItem.BusinessAddress = myOtherItem.BusinessAddress
myItem.BusinessTelephoneNumber = myOtherItem.BusinessTelephoneNumber
myItem.Display
End Sub
If you have the mailbox in your navigation pane, "navigate down the tree". https://stackoverflow.com/a/6116820/1571407
ns.Folders("Personal Folders").Folders("Inbox")
In your code:
Set olkFolder = ns.Folders("name of other mailbox").Folders("Notes")
Whether the folder is in your navigation pane or not you may use CreateRecipient as described in the other answer.

Extract SMTP address from Exchange User in the FROM field of a message

I want to organize messages in folders by domain (and possibly user). I have a script but it can't get the SMTP addresses from exchange users. The below code is an excerpt from the address extractor.
///For each obj in objFolder.Items
If obj.SenderEmailAddress = "EX" Then
Set objSender = obj.Sender
If Not (objSender Is Nothing) Then
Set objExchUser = Sender.GetExchangeUser()
If Not (objExchUser Is Nothing) Then
strSender = objExchUser.PrimarySmtpAddress
End If
End If
Else
If obj.SenderEmailAddress = "" Then
strSender = "ERROR#ERROR.GOV"
Else
MsgBox obj.SenderEmailAddress
'MsgBox obj.PrimarySmtpAddress 'errs out
'MsgBox Sender.GetExchangeUser() 'errs out
'MsgBox Obj.Sender.GetExchangeUser() 'errs out
strSender = obj.PrimarySmtpAddress
End If
End If
///Next
The code works but for those coming from an email group (helpdesk#myexchange.org for example) or spoofed email addresses.
obj.SenderEmailAddress results in "/O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP (removedtextforstack)/CN=RECIPIENTS/CN=7E2removedtextforstackF6-USERNAME"
I believe this is to be expected, but Sender.GetExchangeUser() gives an Object Required error. I don't particularly want 3000 lines of code to dig through AD to try to look up an AD code/user for every email. We use office 365 and it's managed by many people, so things aren't straitforward with accessing the address lists.
MailItem.Sender.GetExchnageUser().ProimarySmtpAddress should work fine for the EX senders, but your code checks the SenderEmailAddress property instead of SenderEmailType. Change it to
If obj.SenderEmailType = "EX" Then
You can also check the PR_SENT_REPRESENTING_SMTP_ADDRESS property (DASL name http://schemas.microsoft.com/mapi/proptag/0x5D02001F, use MailItem.PropertyAccessor.GetProperty) before using the MailItem.Sender property (which is more expensive)
I have a work-around that seems decently efficient.
Const PR_TRANSPORT_MESSAGE_HEADERS = "http://schemas.microsoft.com/mapi/proptag/0x007D001E"
a = Split(obj.PropertyAccessor.GetProperty(PR_TRANSPORT_MESSAGE_HEADERS), vbCrLf)
For i = 0 To UBound(a)
If InStr(1, a(i), "From:", vbTextCompare) = 1 Then
MsgBox Replace(Split(a(i), "<")(1), ">", "")
End If
Next
This pulls the SMTP address directly from the header and does not care what type of message it is.
There still has to be a better way though...