Trying to run a rule to move emails upon arrival from first public email box to second public email box.
Rule is: Apply this rule after the message arrives | Sent to "first public email box" | move it to the "second public email box" folder.
Rule works when run manually but the rule does not work automatically upon emails arriving (research shows there might be some corrupt file, ...).
Trying to make it work via VBA instead. Below macro supposed to run the rules upon a reminder popping up. Reminder pops up, but rules don't run.
Running that macro from a QAT custom button, brings up progress window, and that window shows progress, but emails are still in the first public email box.
Private Sub Application_Reminder(ByVal Item As Object)
If Item.MessageClass <> "IPM.Task" Then
Exit Sub
End If
If Item.Subject = "Run Rules" Then
RunRules
End If
End Sub
Sub RunRules()
Dim olRules As Outlook.Rules
Dim myRule As Outlook.Rule
Dim olRuleNames() As Variant
Dim name As Variant
olRuleNames = Array("Rule A", "Rule B")
Set olRules = Application.Session.DefaultStore.GetRules()
For Each name In olRuleNames()
For Each myRule In olRules
' Rules we want to run
If myRule.name = name Then
myRule.Execute ShowProgress:=True
End If
Next
Next
End Sub
The use of DefaultStore is a hint there are stores other than the default.
Set olRules = Application.Session.DefaultStore.GetRules()
Option Explicit
Private Sub FindStoreWithRules()
Dim colStores As stores
Dim oStore As store
Dim olRules As rules
Dim myRule As Rule
Dim i As Long
Set colStores = Session.stores
For i = 1 To colStores.count
Debug.Print i & ": " & colStores(i)
' On second run
' Enter applicable name then uncomment this line and the End If
' If colStores(i) = "Name of store shown in immediate window" Then
On Error Resume Next
' Where rules not applicable on some stores there is an error.
Set olRules = colStores(i).GetRules()
' Discontinue error bypass as soon as the purpose is served
On Error GoTo 0
If Not olRules Is Nothing Then
For Each myRule In olRules
' Uncomment on second run to see if what rules were found
'Debug.Print " - " & myRule.name
Next
Else
Debug.Print "Rules not applicable in " & colStores(i)
End If
' End If
Next
Debug.Print "Done."
End Sub
Related
I have been tasked to create an automated report system where an report from Google Data Studios are uploaded to specific projects (On a site called Basecamp). The reports always include both a report within the body of the e-mail and an attached PDF file. The are sent to a Gmail account (data studios refuse to schedule towards a non-Google account). The filters within Gmail doesnt really work well with the Basecamp system so I use filters to re-route them towards a Outlook account. There I use rules to send each e-mail towards the correct client within Basecamp.
Here comes the problem, Basecamp shows both the body of the e-mail AND the attached PDF version which makes us show duplicates.
Is there a way to create a macro that first deletes all attachments (or body of an e-mail) and THEN forward the e-mail.
It cant be done manually it have to be a rule that does it automaticaly. Keep in mind that I am not a coder and have never done anything like this so please keep it simple for my dumb brain!
Thank you in advance!
Marcus
PS: I found a code that seems to be what I am after.
Public WithEvents ReceivedItems As Outlook.Items
Private Sub Application_Startup()
Set ReceivedItems = Outlook.Application.Session.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub ReceivedItems_ItemAdd(ByVal Item As Object)
Dim xForwardMail As Outlook.MailItem
Dim xEmail As MailItem
On Error Resume Next
If Item.Class <> olMail Then Exit Sub
Set xEmail = Item
If InStrRev(UCase(xEmail.Subject), UCase("kto feature")) = 0 Then Exit Sub 'change subject text to your need
If xEmail.Attachments.Count = 0 Then Exit Sub
Set xForwardMail = xEmail.Forward
With xForwardMail
.HTMLBody = ""
With .Recipients
.Add "skyyang#addin88.com" 'change address to your own
.ResolveAll
End With
.Send
End With
End Sub
I am trying to get that code to work, and changes the subject to a specific word and then route it to a final e-mail account that then filters out to correct clients. However the code doesnt seem to work, it DOES forward the e-mail but the attachment is still there. The code was found at https://www.extendoffice.com/documents/outlook/5359-outlook-forward-attachment-only.html#a1
It seems you need to modify the code slightly:
Public WithEvents ReceivedItems As Outlook.Items
Private Sub Application_Startup()
Set ReceivedItems = Outlook.Application.Session.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub ReceivedItems_ItemAdd(ByVal Item As Object)
Dim xForwardMail As Outlook.MailItem
Dim xEmail As MailItem
Dim myattachments as Outlook.Attachments
On Error Resume Next
If Item.Class <> olMail Then Exit Sub
Set xEmail = Item
If InStrRev(UCase(xEmail.Subject), UCase("kto feature")) = 0 Then Exit Sub 'change subject text to your need
If xEmail.Attachments.Count = 0 Then Exit Sub
Set xForwardMail = xEmail.Forward
Set myattachments = xForwardMail.Attachments
While myattachments.Count > 0
myattachments.Remove 1
Wend
With xForwardMail
.HTMLBody = ""
With .Recipients
.Add "skyyang#addin88.com" 'change address to your own
.ResolveAll
End With
.Send
End With
End Sub
The Remove method of the Attachments class removes an object from the collection.
I am trying to change incoming emails subject line to only the last 11 characters of the subject line. When I use Item.Subject = Right(Item.Subject,11) it does not work.
Can someone assist?
Full code.
Sub ChangeSubjectForward(Item As Outlook.MailItem)
Item.Subject = Right(Item.Subject, 11)
Item.Save
End Sub
You could create a macro rule then run the below code:
Sub save_to_dir_test1(mymail As MailItem)
Dim strID As String
Dim objMail As Outlook.MailItem
strID = mymail.EntryID
Set objMail = Application.Session.GetItemFromID(strID)
objMail.Subject = Right(m.Subject, 11)
objMail.Save
Set objMail = Nothing
End Sub
For more information, please refer to this link:
Run a Script Rule: Change Subject then Forward Message
Getting the incoming email in outlook via VBA
I found another SO thread that says you can't modify the subject of a message without opening it first. We can use ActiveInspector to get a handle on the Item after we display it. Then we can change it, save it, and close it. I added a check to see if the subject is actually longer than 11 characters before we attempt to truncate it.
Try this:
Public Sub ChangeSubjectForward(ByRef Item As Outlook.MailItem)
Debug.Print Now ' This shows you when the code runs
If Len(Item.Subject) > 11 Then
Debug.Print "Subject is too long. Trimming..." ' This shows that we tried to truncate.
Item.Display 'Force the pop-up
Dim thisInspector As Inspector
Set thisInspector = Application.ActiveInspector
Set Item = thisInspector.CurrentItem ' Get the handle from the Inspector
Item.Subject = Right$(Item.Subject, 11)
Item.Save
Item.Close
End If
End Sub
I'm working within MS Access 2013 and MS Outlook 2013 on Windows 10 and I have a Access DB with a "Navigation Subforms" paradigm that allows sending a single e-mail on two separate occasions.
I'm trying to write code to do the following:
when a new e-mail is sent,
I want to save it as a .msg file on disk automatically.
From what I can tell, it seems the way to do this is via trapping the .ItemAdd event that fires on Outlook Sent Folder within Access, and in there executing the .SaveAs method.
I was trying to implement a solution based on these two answers:
How to Trap Outlook Events from Excel Application
Utilizing Outlook Events From Excel
but I just can't seem to combine the two and make the event fire.
My feeling is that either I'm not referencing/setting things correctly or the execution reaches an end before the e-mail is moved from the Outbox Folder to the Sent Folder, but I'm not sure.
How can I do this?
Thanks for reading, code follows:
My current class module - "cSentFolderItem"
Option Explicit
Public WithEvents myOlItems As Outlook.items
Private Sub Class_Initialize()
Dim oNS As NameSpace
Dim myOL As Outlook.Application
Set myOL = New Outlook.Application
Set oNS = myOL.GetNamespace("MAPI")
Set myOlItems = oNS.GetDefaultFolder(olFolderSentMail).items
End Sub
Private Sub myOlItems_ItemAdd(ByVal Item As Object)
Debug.Print "I got a new item on Sent box!"
Dim myOlMItem As Outlook.MailItem
Set myItem = myOlItems.items(email_subject)
myItem.Display
myItem.SaveAs "C:\Users\XXXXXX\Desktop\mail_test.msg", olMSGUnicode
End Sub
"Regular" code:
Public Function GetApplication(Class As String) As Object
'Handles creating/getting the instance of an application class
Dim ret As Object
On Error Resume Next
Set ret = GetObject(, Class)
If Err.Number <> 0 Then
Set ret = CreateObject(Class)
End If
Set GetApplication = ret
On Error GoTo 0
End Function
Sub Test()
email_subject = "Mail test match string - [aaaa-mm-dd]"
Set myOlItems = New cSentFolderItem 'declare class module object
Dim MyOutlook As Outlook.Application
Set MyOutlook = GetApplication("Outlook.Application") 'trying to get correct application object
'The following code is a dummy e-mail creation, after which I press SEND:
Dim MyMail As Outlook.MailItem
varTo = "target_email#address.com"
varSubject = email_subject
varbody = "test line 1" & vbCrLf & "test line 2" & vbCrLf & "test line 2"
varSubject = Replace(varSubject, "[aaaa-mm-dd]", NOW())
Dim linhas() As String
linhas = Split(varbody, vbCrLf)
bodyHTMLtext = "<body>"
For i = 0 To UBound(linhas) - 1
bodyHTMLtext = bodyHTMLtext & linhas(i) & "<br>"
Next
bodyHTMLtext = bodyHTMLtext & linhas(UBound(linhas))
bodyHTMLtext = bodyHTMLtext & "</body>"
Set MyMail = MyOutlook.CreateItem(OLMAILITEM)
MyMail.To = varTo
MyMail.Subject = varSubject
MyMail.Display
MyMail.HTMLBody = bodyHTMLtext & MyMail.HTMLBody
AppActivate varSubject
'trying to leave Outlook object open:
''Cleanup after ourselves
'Set MyMail = Nothing
''MyOutlook.Quit
'Set MyOutlook = Nothing
End Sub
Ok, after some long hours, I figured it out, and got to the following solution.
My class module "MyOutlook" is:
Option Explicit
Public myOutlookApp As Outlook.Application
Public mySentFolder As Outlook.Folder
Public WithEvents myItems As Outlook.items
Private Sub Class_Initialize()
Set myOutlookApp = GetApplication("Outlook.Application")
Dim oNS As NameSpace
Set oNS = myOutlookApp.GetNamespace("MAPI")
Set mySentFolder = oNS.GetDefaultFolder(olFolderSentMail)
Set myItems = mySentFolder.items
End Sub
Private Sub myItems_ItemAdd(ByVal Item As Object)
Debug.Print "Got_EMAIL!!! Looking for subject = " & email_subject
'"e-mail_subject" is Public a string, assigned in another part of the program
If Item.Subject = email_subject Then
Item.SaveAs "C:\Users\640344\Desktop\mail_test.msg", olMSGUnicode
End If
End Sub
Where GetApplication is:
Function GetApplication(Class As String) As Object
'Handles creating/getting the instance of an application class
'If there exists one already (in my case, Outlook already open),
'it gets its name, else it creates one
Dim ret As Object
On Error Resume Next
Set ret = GetObject(, Class)
If Err.Number <> 0 Then
Set ret = CreateObject(Class)
If Class = "Outlook.Application" Then
'Outlook wasn't opened, so open it
ret.Session.GetDefaultFolder(olFolderInbox).Display
ret.ActiveExplorer.WindowState = olMaximized
ret.ActiveExplorer.WindowState = olMinimized
End If
End If
Set GetApplication = ret
On Error GoTo 0
End Function
Note that I added the 3 lines of code after 'Outlook wasn't opened, so open it because otherwise I would get an error. It's not a bad idea for my users that the program opens Outlook, anyway.
On the "regular" code part of my project, outside any procedure, I declare:
Public myOutlook As myOutlook
Then, on my project's "main" sub:
Set myOutlook = New myOutlook
'[...]
'Code where entire program runs
'[...]
Set myOutlook = Nothing
This way, myOutlook object (and its variables) "lives" the entire time the program (with its Navigation Forms) is running, and is waiting to trap _ItemAdd events on the default Sent Folder of Outlook.
Note that I look only for e-mails with subject equal to the email_subject string, because I don't want to save all sent e-mails, just the one sent by using the program, and I have code to assign my desired subject to that string.
Very similar requirement at first glance to Luke123's 'Append a Tag to Outlook' request.
Here, I need to append an autonumber Task ID (which is specific only to this requirement) into the Subject line of an Outlook Exchange (shared) mailbox.
This therefore needs to a) autonumber sequentially and b) run automatically as the e-mails land.
Pretty certain rules for shared mailboxes run server-side and are locked down by the business.
All ideas/help gratefully received.
This code will most likely need to be tweaked but should do what you want. You may need to take the existing Inbox items and give them a task ID in order to get the ball rolling. See my comments for explanation of the code as well as places you need to edit.
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim olApp As Outlook.Application
Set olApp = Outlook.Application
' edit this line to reflect the actual mailbox name as displayed in Outlook
Set Items = Session.Folders("Mailbox - My Shared Mailbox").Folders("Inbox")
End Sub
Private Sub Items_ItemAdd(ByVal item As Object)
On Error GoTo ErrorHandler
Dim msg As Outlook.mailItem
Dim firstObj As Object
Dim i As Long
Dim firstMsg As Outlook.mailItem
Dim currentTaskID As String
Dim nextTaskID As Long
If TypeName(item) = "MailItem" Then ' it's an email
Set msg = item
' get first email from Inbox to determine next task ID
Do Until TypeName(firstObj) = "MailItem" Or i = Session.Folders("Mailbox - My Shared Mailbox").Folders("Inbox").Items.Count
i = i + 1
' might have to start at item #2?
Set firstObj = Session.GetDefaultFolder(olFolderInbox).Items(i)
Loop
' typecast the object to mailitem for Intellisense
If TypeName(firstObj) = "MailItem" Then
Set firstMsg = firstObj
Else
' display messagebox?
Goto ProgramExit
End If
' get task id and calculate next value, let's assume it's the last three chars of subject
' Ex: Subject: Incoming Email - TaskId: 001
currentTaskID = Right$(firstMsg.Subject, 3)
nextTaskID = CLng(currentTaskID) + 1
' put next task ID number into new email's subject
With msg
.Subject = msg.Subject & " - TaskId: " & nextTaskID
.Save
End With
End If
ProgramExit:
Exit Sub
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
Resume ProgramExit
End Sub
I use a outlook rule to process incoming mail via a VBA macro.
in the vba various actions are triggerd to process attachments of the incoming mail.
The problem is, there somethimes is a stack of e-mails that need to be processed.
I cant seem to find a way how to trigger the one by one.
I want to wait a few seconds before processing the next mail, if there is a stack.
Putting a sleep method in the macro doesnt seem to have effect. the rule doesnt seem to wait for the previous message to be done.
My method i something like:
Is there a way to accomplish this behaviour?
Private Sub ProcessMail(ByVal Item As Object)
Dim objNS As Outlook.NameSpace
Set objNS = GetNamespace("MAPI")
If TypeOf Item Is Outlook.MailItem Then
Dim Msg As Outlook.MailItem
DoProcessingMethod
End If
End If
End Sub
Putting a wait or sleep in the method doesnt cause it to be processed one by one.
GD Arnold,
You could indeed use the ItemAdd option as per #Brett's answer.
I use a similar process to automatically upload received data (as attachment in an email) and upload this to a MySQL database. The action is triggered by the ItemAdd method and mails are checked one-by-one.
Simplified instructions:
Add a Class to your VBA code named "EventClassModule"
In your class, type
Public WithEvents dItems As Outlook.Items
In your ThisOutlookSession make a sub that registers the event_handler:
Sub Register_Event_Handler()
Set myClass.dItems = Outlook.Items
End Sub
In your ThisOutlookSession make a sub that handles the ItemAdd event as below:
Private Sub dItems_ItemAdd(ByVal newItem As Object)
On Error GoTo ErrorHandler
Dim msg As Outlook.MailItem
If newItem.Class = olMail Then
Set msg = newItem
'Do something with the msg item, check rules, check subject, check whatever
'This will process messages when the arrive in your mailbox one by one.
End If
ProgramExit:
Exit Sub
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
Resume ProgramExit
End Sub
These steps should provide you with a sub that is triggered when a new mail arrives.
You could then call a function/sub like below.
The below sub runs all rules based on an optional ruleSet variable, it checks the rule.Name against the ruleSet and if the ruleSet string exists in the rule.Name then it executes some code. This way you can have multiple rules and only execute some of them based on which 'ruleSet' they are part of. You can define that by altering their name.
It's a refinement of the 'Run Rules' option in Outlook.
Some of this code came frome here: Setting VBA to read personal inbox
Sub runRules(Optional ruleSet As String)
Dim olStore As Outlook.Store
Dim myRules As Outlook.Rules
Dim tmpInbox As Outlook.Folder
Dim tmpSent As Outlook.Folder
Dim rl As Outlook.Rule
'On Error Resume Next
'toTmpBox (ruleSet)
' get default store (where rules live)
Set olStore = Application.Session.DefaultStore
With olStore
Set tmpInbox = .GetDefaultFolder(olFolderInbox) '.Folders("tmpInbox")
Set tmpSent = .GetDefaultFolder(olFolderSentMail) '.Folders("tmpSentBox")
End With
' get rules
Set myRules = olStore.GetRules
' iterate through all the rules
For Each rl In myRules
Debug.Print rl.Conditions.Body.Enabled & " " & rl.Conditions.Body.Text
If InStr(LCase(rl.Name), ruleSet) > 0 And (rl.Enabled) Then
rl.Execute ShowProgress:=True, Folder:=tmpInbox
If ruleSet = "autorun" Then
rl.Execute ShowProgress:=True, Folder:=olStore.GetDefaultFolder(olFolderSentMail)
End If
ruleList = ruleList & vbCrLf & rl.Name
End If
Next
' tell the user what you did
ruleList = "These rules were executed " & _
vbCrLf & ruleList
MsgBox ruleList, vbInformation, "Macro: RunMyRules"
CleanUp:
Set olStore = Nothing
Set tmpInbox = Nothing
Set tmpSent = Nothing
Set rl = Nothing
Set myRules = Nothing
End Sub
I have come across a similar problem. In my case my tool would run at regular time interval and each time I had to capture new emails only. Now new emails could be one or multiple. the solution I found was as given below.
Each time the tool would run. it will capture the new emails and just mark a simple ',' or '|' anything of your choice at the end of the subject in such a way that no one will notice. Now next time when the tool runs it checks if the emails received for the entire day or two (based on your requirements) has those markers or not.
This solution works if the email communication is one way. If we use these email for chain emails then their is another solution.
Here you will have to save the time max time of emails captured in the last run. Now each time you run you just have to run it for the entire day and put an if statement that is should be greater then time last captured.
Now to store the max time you might need to create a folder and an email. The email can help you to store the each time the run happens
item.subject = maxtime
item.save