Remove duplicates if team email address in the recipients - vba

We have a team email address that we CC for most correspondence, and then we all get a copy of all emails.
The problem is when we then reply all, and a team member has already been in the email chain that person will get the email 2 times.
This is what I tried.
Private Sub RemoveRecipientsWhenItemSend(Item As Outlook.MailItem)
Dim RemoveAddrList As VBA.Collection
Dim InfoAddrList As VBA.Collection
Dim Recipients As Outlook.Recipients
Dim aRecipient As Outlook.Recipient
Dim bRecipient As Outlook.Recipient
Dim i
Dim j
Dim a
Dim b
Dim info As Boolean
info = False
Set RemoveAddrList = New VBA.Collection
Set InfoAddrList = New VBA.Collection
InfoAddrList.Add "team#company.com"
RemoveAddrList.Add "member1#company.com"
RemoveAddrList.Add "member2#company.com"
Set Recipients = Item.Recipients
For i = Recipients.Count To 1 Step -1
Set aRecipient = Recipients.Item(i)
For j = 1 To InfoAddrList.Count
If LCase$(aRecipient.Address) = LCase$(InfoAddrList(j)) Then
For a = Recipients.Count To 1 Step -1
Set bRecipient = Recipients.Item(a)
For b = 1 To RemoveAddrList.Count
If LCase$(aRecipient.Address) = LCase$(RemoveAddrList(b)) Then
Recipients.Remove i
Exit For
End If
Next
Next
Exit For
End If
Next
Next
End Sub
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
On Error Resume Next
RemoveRecipientsWhenItemSend Item
End Sub

A few Debug.Print statements proved helpful.
Option Explicit
Private Sub RemoveRecipientsWhenItemSend(Item As Outlook.mailitem)
Dim RemoveAddrList As VBA.Collection
Dim InfoAddrList As VBA.Collection
Dim Recipients As Outlook.Recipients
Dim aRecipient As Outlook.Recipient
Dim bRecipient As Outlook.Recipient
Dim i
Dim j
Dim a
Dim b
Dim info As Boolean
info = False
Set RemoveAddrList = New VBA.Collection
Set InfoAddrList = New VBA.Collection
InfoAddrList.Add "team#company.com"
RemoveAddrList.Add "member1#company.com"
RemoveAddrList.Add "member2#company.com"
Set Recipients = Item.Recipients
For i = Recipients.count To 1 Step -1
Set aRecipient = Recipients.Item(i)
For j = 1 To InfoAddrList.count
Debug.Print LCase$(aRecipient.Address)
Debug.Print LCase$(InfoAddrList(j))
If LCase$(aRecipient.Address) = LCase$(InfoAddrList(j)) Then
For a = Recipients.count To 1 Step -1
'Set bRecipient = Recipients.Item(a)
Set aRecipient = Recipients.Item(a)
For b = 1 To RemoveAddrList.count
Debug.Print vbCr & " a: " & a
Debug.Print " LCase$(aRecipient.Address): " & LCase$(aRecipient.Address)
Debug.Print " LCase$(RemoveAddrList(b)): " & LCase$(RemoveAddrList(b))
If LCase$(aRecipient.Address) = LCase$(RemoveAddrList(b)) Then
'Recipients.Remove i
Recipients.Remove a
Exit For
End If
Next
Next
Exit For
End If
Next
Next
End Sub
Private Sub RemoveRecipientsWhenItemSend_test()
RemoveRecipientsWhenItemSend ActiveInspector.currentItem
End Sub

Here is something I use to remove the duplicate recipients.
Set olemail = olapp.CreateItemFromTemplate(OutlookTemplate)
With olemail
' other stuff
' check duplicate recipients
' first resolve email address per global address book
For Each Recipient In .Recipients
Recipient.Resolve
Next
' go through each recipients and check for dup
If .Recipients.count > 1 Then
For i = .Recipients.count To 2 Step -1
For j = i - 1 To 1 Step -1
If .Recipients(i) = .Recipients(j) Then
.Recipients.Remove (i)
i = i - 1
End If
Next j
Next i
End If
end with

Related

Extract Outlook UserDefinedProperties field

I add UserDefinedProperties in Outlook with the below code
Sub AddStatusProperties()
Dim objNamespace As NameSpace
Dim objFolder As Folder
Dim objProperty As UserDefinedProperty
Set objNamespace = Application.GetNamespace("MAPI")
Set objFolder = objNamespace.GetDefaultFolder(olFolderInbox)
With objFolder.UserDefinedProperties
Set objProperty = .Add("MyNotes1", olText, 1)
End With
End Sub
The user can add a value to MyNotes1 field in any email.
Public Sub EditField()
Dim obj As Object
Dim objProp As Outlook.UserProperty
Dim strNote As String, strAcct As String, strCurrent As String
Dim propertyAccessor As Outlook.propertyAccessor
Set obj = Application.ActiveExplorer.Selection.Item(1)
On Error Resume Next
Set UserProp = obj.UserProperties.Find("MyNotes1")
If Not UserProp Is Nothing Then
strCurrent = obj.UserProperties("MyNotes1").Value
End If
Dim varArrayList As Variant
Dim varArraySelected As Variant
varArrayList = Array("value1", "value2", "value3")
varArraySelected = SelectionBoxMulti(List:=varArrayList, Prompt:="Select one or more values", _
SelectionType:=fmMultiSelectMulti, Title:="Select multiple")
If Not IsEmpty(varArraySelected) Then 'not cancelled
For i = LBound(varArraySelected) To UBound(varArraySelected)
If strNote = "" Then
strNote = varArraySelected(i)
Else
strNote = strNote & ";" & varArraySelected(i)
End If
Next i
End If
Set objProp = obj.UserProperties.Add("MyNotes1", olText, True)
objProp.Value = strNote
obj.Save
Err.Clear
Set obj = Nothing
End Sub
I need to extract all email properties including the values available under MyNotes field to Excel. How do I recall MyNotes1 values?
This is the Excel code. The part I miss is "myArray(6, i - 1) = item.?????".
Public Sub getEmails()
On Error GoTo errhand:
Dim outlook As Object: Set outlook = CreateObject("Outlook.Application")
Dim ns As Object: Set ns = outlook.GetNamespace("MAPI")
'This option open a new window for you to select which folder you want to work with
Dim olFolder As Object: Set olFolder = ns.PickFolder
Dim emailCount As Long: emailCount = olFolder.Items.Count
Dim i As Long
Dim myArray As Variant
Dim item As Object
ReDim myArray(6, (emailCount - 1))
For i = 1 To emailCount
Set item = olFolder.Items(i)
If item.Class = 43 And item.ConversationID <> vbNullString Then
myArray(0, i - 1) = item.Subject
myArray(1, i - 1) = item.SenderName
myArray(2, i - 1) = item.To
myArray(3, i - 1) = item.CreationTime
myArray(4, i - 1) = item.ConversationID
myArray(5, i - 1) = item.Categories
'myArray(6, i - 1) = item.?????
End If
Next
With ActiveSheet
.Range("A1") = "Subject"
.Range("B1") = "From"
.Range("C1") = "To"
.Range("D1") = "Created"
.Range("E1") = "ConversationID"
.Range("F1") = "Category"
.Range("G1") = "MyNote"
.Range("A2:G" & (emailCount + 1)).Value = TransposeArray(myArray)
End With
Exit Sub
errhand:
Debug.Print Err.Number, Err.Description
End Sub
You already have code that retrieves that property
Set UserProp = item.UserProperties.Find("MyNotes1")
If Not UserProp Is Nothing Then
myArray(6, i - 1) = UserProp.Value
End If

How to update a meeting? Error: Argument Not Optional

After a recent Outlook update, this code started returning
"Compile Error:Argument Not Optional"
The line Set Travel = Items.Add is triggering the error.
Public Sub AddTravelTime()
Dim coll As VBA.Collection
Dim obj As Object
Dim Appt As Outlook.AppointmentItem
Dim Travel As Outlook.AppointmentItem
Dim Items As Outlook.Items
Dim Before&, After&
Dim Category$, Subject$
'1. Block minutes before and after the appointment
Before = 30
After = 30
'3. Assign this category
Category = "Meeting Cushion Time"
Set coll = GetCurrentItems
If coll.Count = 0 Then Exit Sub
For Each obj In coll
If TypeOf obj Is Outlook.AppointmentItem Then
Set Appt = obj
If TypeOf Appt.Parent Is Outlook.AppointmentItem Then
Set Items = Appt.Parent.Parent.Items
Else
Set Items = Appt.Parent.Items
End If
'4. Use the main appointment's subject
Subject = "Meeting Cushion Time"
If Before > 0 Then
Set Travel = Items.Add ' <------ Compile Error
Travel.Subject = Subject
Travel.Start = DateAdd("n", -Before, Appt.Start)
Travel.Duration = Before
Travel.Categories = Category
Travel.Save
End If
If After > 0 Then
Set Travel = Items.Add
Travel.Subject = Subject
Travel.Start = Appt.End
Travel.Duration = After
Travel.Categories = Category
Travel.Save
End If
End If
Next
End Sub
Private Function GetCurrentItems(Optional IsInspector As Boolean) As VBA.Collection
Dim coll As VBA.Collection
Dim Win As Object
Dim Sel As Outlook.Selection
Dim obj As Object
Dim i&
Set coll = New VBA.Collection
Set Win = Application.ActiveWindow
If TypeOf Win Is Outlook.Inspector Then
IsInspector = True
coll.Add Win.CurrentItem
Else
IsInspector = False
Set Sel = Win.Selection
If Not Sel Is Nothing Then
For i = 1 To Sel.Count
coll.Add Sel(i)
Next
End If
End If
Set GetCurrentItems = coll
End Function
The goal is to add a time buffer before and after each meeting.
The travel object is defined as an instance of the AppointmentItem class:
Dim Travel As Outlook.AppointmentItem
But in the code, you are trying to set another item type:
Set Travel = Items.Add
The default item type for a folder will be returned. Of course, it depends on the folder. But I suspect it is not an appointment item in your case.
If "Items.Add" is asking for more arguments, the implication is the items in that collection have more non-optional arguments, so the default items for that folder are not appointment items.
Option Explicit
Public Sub AddTravelTime()
Dim coll As VBA.Collection
Dim obj As Object
Dim Appt As AppointmentItem
Dim Travel As AppointmentItem
Dim Items As Items
Dim Before As Long
Dim After As Long
Dim Subject As String
' Block minutes before and after the appointment
Before = 30
After = 30
Set coll = GetCurrentItems
If coll.Count = 0 Then Exit Sub
For Each obj In coll
If TypeOf obj Is outlook.AppointmentItem Then
Set Appt = obj
Debug.Print "Appt.Subject: " & Appt.Subject
'https://learn.microsoft.com/en-us/office/vba/api/outlook.folder.defaultitemtype
'https://learn.microsoft.com/en-us/office/vba/api/outlook.olitemtype
If TypeOf Appt.Parent Is outlook.AppointmentItem Then
' recurring appointment
Debug.Print "Appt.Parent.Parent: " & Appt.Parent.Parent
Debug.Print Appt.Parent.Parent.DefaultItemType ' 1 = olAppointmentItem
If Appt.Parent.Parent.DefaultItemType = olAppointmentItem Then
Set Items = Appt.Parent.Parent.Items
Else
Debug.Print Appt.Parent.Parent.DefaultItemType
MsgBox "Default item in " & Appt.Parent.Parent & " is not appointment item."
Exit Sub
End If
Else
Debug.Print "Appt.Parent: " & Appt.Parent
Debug.Print Appt.Parent.DefaultItemType ' 1 = olAppointmentItem
If Appt.Parent.DefaultItemType = olAppointmentItem Then
Set Items = Appt.Parent.Items
Else
Debug.Print Appt.Parent.DefaultItemType
MsgBox "Default item in " & Appt.Parent & " is not appointment item."
Exit Sub
End If
End If
Subject = "Meeting Cushion Time"
If Before > 0 Then
Set Travel = Items.Add
Travel.Subject = Subject
Travel.Start = DateAdd("n", -Before, Appt.Start)
Travel.Duration = Before
Travel.Save
End If
If After > 0 Then
Set Travel = Items.Add
Travel.Subject = Subject
Travel.Start = Appt.End
Travel.Duration = After
Travel.Save
End If
End If
Next
End Sub
Private Function GetCurrentItems(Optional IsInspector As Boolean) As VBA.Collection
Dim coll As VBA.Collection
Dim Win As Object
Dim Sel As Selection
Dim obj As Object
Dim i As Long
Set coll = New VBA.Collection
Set Win = ActiveWindow
If TypeOf Win Is outlook.Inspector Then
IsInspector = True
coll.Add Win.CurrentItem
Else
IsInspector = False
Set Sel = Win.Selection
If Not Sel Is Nothing Then
For i = 1 To Sel.Count
coll.Add Sel(i)
Next
End If
End If
Set GetCurrentItems = coll
End Function

How to use select names dialog with VBA for send Email for Recipients more than one?

I'm create small VBA script for send Email with VBA.I'm create Dialog box for identify Recipients.It work well but it can get one Recipient.Like this :
This's my output.It show only one last Recipient.
This's my Code:
Sub cc_BCC()
Dim oMsg As Outlook.MailItem
Set oMsg = Outlook.Application.CreateItem(olMailItem)
Dim oDialog As Outlook.SelectNamesDialog
Set oDialog = Outlook.Application.Session.GetSelectNamesDialog
Dim oAL As Outlook.AddressList
Dim oContacts As Outlook.Folder
Dim cEI As String
Dim c As Outlook.AddressEntry
Dim olRecipient As Outlook.Recipient
Set oContacts = _
Outlook.Application.Session.GetDefaultFolder(olFolderContacts)
For Each oAL In Outlook.Application.Session.AddressLists
If oAL.AddressListType = olOutlookAddressList Then
If oAL.GetContactsFolder.EntryID = _
oContacts.EntryID Then
Exit For
End If
End If
Next
With oDialog
.Caption = "Select Customer Contact"
.ToLabel = "Customer C&ontact"
.NumberOfRecipientSelectors = olShowTo
.InitialAddressList = oAL
.AllowMultipleSelection = True
.Recipients = oMsg.Recipients
If .Display Then
'Recipients Resolved
For Each olRecipient In .Recipients
cEI = olRecipient.EntryID 'entry id of selected contact
Set c = Outlook.Application.Session.GetAddressEntryFromID(cEI)
Worksheets("Dialog").Range("A15") = c.GetContact.FirstName & " " & c.GetContact.LastName
Next
End If
End With
End Sub
And I need this output.Like this:
How can I fix this problem?
Firstly, do not hardcode the cell name ("A15"). Secondly, do not call GetAddressEntryFromID - just use Recipient.AddressEntry. Thirdly, do not assume GetContact will return a non-null value, it only works if the item comes from your Contacts folder, it will not work for the GAL entries.
For i = 1 to .Recipients.Count
set olRecipient = .Recipients(i)
Worksheets("Dialog").Range("A" & i) = olRecipient.name
Next

Script moves only a couple of 'Inbox' items on each execution

I have the follwing VBA script for Outlook that should move emails to the Archives folder (that are not categorized in one of the special categories). It both works and not. I mean it moves some emails but skips the others so I have to run it mulitple times until the Inbox is cleaned-up. I don't understand why it behaves this way. It doesn't throw any exceptions it just doesn't do its job for all items. Can you see anything suspicios here?
Option Explicit
Sub CleanUpInbox()
Dim ns As Outlook.NameSpace
Set ns = GetNamespace("MAPI")
Dim inbox As Outlook.Folder: Set inbox = ns.GetDefaultFolder(olFolderInbox)
Dim archive As Outlook.Folder: Set archive = ns.Folders("my#mailbox.abc").Folders("Archives").Folders("2018")
Dim maxDiffInDays As Integer: maxDiffInDays = 14
Dim today As Date: today = DateValue(now())
On Error GoTo bang
Dim mail As Variant ' Outlook.MailItem
For Each mail In inbox.Items
If mail Is Nothing Then
GoTo continue
End If
Dim receivedOn As Date: receivedOn = DateValue(mail.ReceivedTime)
Dim diff As Integer: diff = DateDiff("d", receivedOn, today)
Dim isOld As Boolean: isOld = True ' diff > maxDiffInDays
If isOld Then
'Debug.Print diff
'Debug.Print mail.Subject
'Debug.Print mail.Categories
Dim isPinned As Boolean: isPinned = InStr(mail.Categories, "PINNED")
Dim isTTYL As Boolean: isTTYL = InStr(mail.Categories, "TTYL")
If LinqAll(False, isPinned, isTTYL) Then
Debug.Print mail.Subject
mail.Move archive
End If
End If
GoTo continue
bang:
Debug.Print "bang!"
Debug.Print Err.Description
continue:
Next
End Sub
Function LinqAll(ByVal Expected As Boolean, ParamArray Values() As Variant) As Boolean
Dim x As Variant
For Each x In Values
If x <> Expected Then
LinqAll = False
Exit Function
End If
Next
LinqAll = True
End Function
Function LinqAny(ByVal Expected As Boolean, ParamArray Values() As Variant) As Boolean
Dim x As Variant
For Each x In Values
If x = Expected Then
LinqAny = True
Exit Function
End If
Next
LinqAny = False
End Function
Not sure whether I miss something here, but your code seems to handle any mail as old, for you set isOld to true within the loop. Is there a special reason for declaring isPinedand isTTYLeach loop? Have you tried:
Sub CleanUpInbox()
Dim ns As Outlook.Namespace
Dim inbox As Outlook.Folder: Set inbox = ns.GetDefaultFolder(olFolderInbox)
Dim archive As Outlook.Folder: Set archive = ns.Folders("my#mailbox.abc").Folders("Archives").Folders("2018")
Dim maxDiffInDays As Integer: maxDiffInDays = 14
Dim today As Date: today = DateValue(Now())
Dim mail As Variant ' Outlook.MailItem
Dim receivedOn As Date
Dim diff As Integer
Dim isOld As Boolean
Dim isPinned As Boolean
Dim isTTYL As Boolean
Set ns = GetNamespace("MAPI")
On Error GoTo bang
For Each mail In inbox.Items
If mail Is Nothing Then
GoTo continue
End If
isOld = False
receivedOn = DateValue(mail.ReceivedTime)
diff = DateDiff("d", receivedOn, today)
If diff > maxDiffInDays Then
isOld = True
End If
isPinned = InStr(mail.Categories, "PINNED")
isTTYL = InStr(mail.Categories, "TTYL")
If LinqAll(False, isPinned, isTTYL) Then
Debug.Print mail.Subject
mail.Move archive
End If
GoTo continue
bang:
Debug.Print "bang!"
Debug.Print Err.Description
continue:
Next
End Sub
I've solved it. You must not use Items in a For Each loop and at the samve time .Move its items. It's like modifying the loop collection in C#. The only difference is that C# is throwing a nice exception while VBA just reduces the number of items and then just stops :-o
Instead, I used Do While and two counters. One that counts the processed items and the other that is the current index for Items. Now it processes everything.
Sub CleanUpInbox2()
' ... other variables
Dim processCount As Integer
Dim itemIndex As Integer: itemIndex = 1
Dim itemCount As Integer: itemCount = inbox.Items.Count
Do While processCount < itemCount
processCount = processCount + 1
Set mail = inbox.Items(itemIndex)
' ... body
If LinqAll(False, isPinned, isTTYL) Then
Debug.Print mail.Subject
mail.Move archive
moveCount = moveCount + 1
Else
itemIndex = itemIndex + 1
End If
bang:
Debug.Print "bang!"
Debug.Print Err.Description
continue:
Loop
Debug.Print "Emails processed: " & processCount
Debug.Print "Emails moved: " & moveCount
End Sub
I tried to copy Items first but I didn't succeed with that (apparently there is no new Outlook.Items) so I use indexes.

List the filenames of attachments

In this exam I get the number of "attachment file" for an email in draft data.
Is there any way to get the name of this file in a msgbox or combobox or anything?
Private Sub CommandButton2_Click()
Dim a As Attachments
Dim myitem As Folder
Dim myitem1 As MailItem
Set myitem = Session.GetDefaultFolder(olFolderDrafts)
Dim i As Integer
For i = 1 To myitem.Items.Count
If myitem.Items(i) = test1 Then
Set myitem1 = myitem.Items(i)
Set a = myitem1.Attachments
MsgBox a.Count
End If
Next
End Sub
Private Sub CommandButton2_Click()
Dim a As Attachments
Dim myitem As Folder
Dim myitem1 As MailItem
Dim j As Long
Dim i As Integer
Set myitem = Session.GetDefaultFolder(olFolderDrafts)
For i = 1 To myitem.Items.Count
If myitem.Items(i) = test1 Then
Set myitem1 = myitem.Items(i)
Set a = myitem1.Attachments
MsgBox a.Count
' added this code
For j = 1 To myitem1.Attachments.Count
MsgBox myitem1.Attachments.Item(i).DisplayName ' or .Filename
Next j
End If
Next i
End Sub