Макрос для сохранения локальной копии почты (и переименования локальной копии)

Компания, в которой я работаю, вручную сохраняет определенные запросы (отправляемые по почте) на общий диск, переименовывая их следующим образом: «ГГГГММДД_Имя_Фамилия». Письма сохраняются как .msg

Так как мы получаем около сотни таких в неделю, я хотел бы макросировать это, чтобы не тратить время впустую.

Статья здесь: Макрос Outlook VBA для сохранения копий писем в локальной папке объясняет, как сохранять файлы локально, но я хотел бы сделать следующие дополнения: - Переименуйте копию перед ее сохранением на общий диск (при необходимости вручную) - Выберите общий путь, по которому его нужно сохранить (предпочтительно раскрывающийся список с тремя вариантами). - создайте правильную пользовательскую форму для этого

Если кто-нибудь может помочь с кодом или предоставить мне учебники/руководства о том, как сделать это самостоятельно, я был бы очень признателен.

P.S. только начал использовать и создавать макросы неделю назад. Еще очень новичок. буду очень признателен за любую ссылку на хороший учебник для разработчиков, независимо от того, отвечает ли он на мои вопросы.

Спасибо, парни!

Использовал код, описанный в статье, как таковой:

Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
    Call SaveACopy(Item)
End Sub

Sub SaveACopy(Item As Object)
    Const olMsg As Long = 3

    Dim m As MailItem
    Dim savePath As String

    If TypeName(Item) <> "MailItem" Then Exit Sub

    Set m = Item

    savePath = "c:\users\your_user_name\desktop\"  '## Modify as needed
    savePath = savePath & m.Subject & Format(Now(), "yyyy-mm-dd-hhNNss")
    savePath = savePath & ".msg"


    m.SaveAs savePath, olMsg


End Sub

Обновление: с помощью макроса, предоставленного Тони Даллимором, мне удалось определить, что .SenderName — это основная информация, которая мне нужна для обработки почты. Все, что мне нужно сейчас, это заменить пробелы в этом выводе символами подчеркивания и добавить дату в обратном порядке перед ним, чтобы иметь мое имя файла.

Большое спасибо Тони Даллимору за постоянную помощь в этом проекте.

Поскольку это кажется несколько запутанным, оглядываясь на мой первоначальный вопрос, я попытаюсь уточнить:

Я получаю около 100 писем в неделю, информирующих нас об одобрении определенных запросов пользователей. Политика компании заключается в сохранении этих писем в формате .msg на общем диске, используемом для администрирования, перед обработкой запроса. Имя файла этих сообщений должно быть таким: «ГГГГММДД_FIRSTNAME_LASTNAME.msg» (где ГГГГ — год, ММ — месяц, а ДД — день, когда мы получили эти письма)

Мы получаем три основных «типа» таких писем, сохраненных в разных местах, но с использованием одного и того же имени файла соответственно.

Что мне нужно, так это макрос или набор макросов, которые могут сохранять эти письма на правильном сетевом диске в правильном формате одним нажатием кнопки или с минимальным количеством кликов/ручного ввода.

Я решил использовать .SenderName и .Senton, так как они, кажется, дают мне большую часть того, что мне нужно.

savePath — это полный путь и имя файла сохраненного сообщения. В настоящее время имя .Subject, за которым следует сэкономленное время. Обычно я использую .ReceivedTime вместо Now(). Вы можете изменить путь и/или имя файла на что-то другое. Какое имя ты хочешь?
Tony Dallimore 08.04.2019 14:41

Я не могу найти учебник в Интернете или в книге, который мне нравится. Доступно множество высококачественных подпрограмм, но иногда вам нужна некоторая предыстория, чтобы понять их. Когда у меня будет время, я планирую попробовать сам. Помогает ли этот мой ответ: stackoverflow.com/a/8699250/973283.

Tony Dallimore 08.04.2019 14:47

Когда вы вводите свой вопрос, над окном редактирования есть несколько опций. Один из вариантов — { }. Используйте эту опцию для форматирования кода. Я сделал это для вас. Ранее первая и последняя строки кода не входили в блок кода.

Tony Dallimore 08.04.2019 15:10

Outlook поддерживает ряд событий. Вы используете ItemSend, но в вашем вопросе говорится, что вы «получаете» эти электронные письма. Следует ли вам использовать событие ItemAdd, которое обрабатывает новые записи в папке «Входящие»? Я предпочитаю использовать правило со сценарием, а не событие ItemAdd.

Tony Dallimore 08.04.2019 15:14

Спасибо за ответы! Я буду использовать {} в будущем, спасибо за совет.

Tman 08.04.2019 17:20

Что мне особенно нужно, так это сделать копию полученной почты и сохранить эту копию на общем диске. Форматом хранения будет дата, но в обратном порядке (ГГГГММДД), за которой следует символ подчеркивания, а также имя и фамилия пользователя, разделенные символами подчеркивания. Вероятно, это должна быть ручная запись, так как письма автоматически генерируются системой и включают в себя скриншот с подробной информацией о рассматриваемом пользователе. Будет ли ItemAdd обеспечивать такую ​​же функциональность? Еще раз спасибо за ответы.

Tman 08.04.2019 17:24

Мой ответ должен определить, доступны ли имя и фамилия в свойствах электронной почты. Какая дата: дата отправки, дата получения, дата сохранения, дата в теле письма или какая-то другая дата? Папка для сохранения имеют должна быть выбрана пользователем? Я не был бы счастлив, если бы в случайное время в течение дня меня прерывали, чтобы решить, какая папка подходит для электронной почты.

Tony Dallimore 08.04.2019 22:41

В своем ответе я даю краткое введение в события и макросы событий. Отправка электронного письма вызывает событие ItemSend. Получение электронного письма или его перемещение в другую папку вызывает событие ItemAdd. Код для разных событий одинаков; это просто случай определения соответствующего триггера для ваших нужд.

Tony Dallimore 08.04.2019 22:55

Обратите внимание, что никому не сообщается, что вы отредактировали свой вопрос. Это «старый» вопрос, поэтому я сомневаюсь, что люди его перечитывают. Вы получите сообщение, если на ваш вопрос будет опубликован ответ или комментарий. Отвечающий получает сообщение, если к его ответу публикуется комментарий. Включение Xyyyy, где X — это @, а yyyy — это имя пользователя в комментарии, гарантирует, что пользователь yyyy получит сообщение. Я проверяю ответы и комментарии в течение дня или двух после их публикации, поэтому я заметил ваше редактирование.

Tony Dallimore 09.04.2019 18:19

Вы ищете имя файла в формате «ГГГГММДД_John_Doe.msg». Вы говорите, что получаете около 100 таких сообщений в неделю, что означает, что около 20 сообщений будут сохранены с одним и тем же именем файла. Если они генерируются системой, существует большая вероятность того, что за одну секунду могут быть сгенерированы два, поэтому добавление времени может оказаться недостаточным для обеспечения уникальности имен. В имя файла необходимо включить порядковый номер.

Tony Dallimore 09.04.2019 18:52

Я понимаю необходимость иметь единый архив всех этих сообщений. Вы говорите: «Я получаю около 100 электронных писем в неделю, информирующих нас…». Вы имеете в виду, что всего 100 сообщений в неделю, или вы и несколько ваших коллег получаете по 100 сообщений в неделю? Даже если это всего 100 сообщений, это все равно более 5000 в год, распределенных по трем папкам. Если вам понадобится обратиться к одному из этих сообщений, поиск правильного будет трудоемким процессом. Я бы не рассматривал такой архив без индекса и без подпапок по месяцам.

Tony Dallimore 09.04.2019 18:56

Как вы определяете эти сообщения? Есть ли у них определенный предмет или определенная строка внутри тела? Как определить, какая папка подходит для данного сообщения?

Tony Dallimore 09.04.2019 18:56

Да, таких писем еженедельно приходит довольно много (всего 100). Мы отвечаем за эти запросы для всего региона EMEA, поэтому я не удивлен, увидев 5000 в год. Да, запросы разбиты по годам - ​​каждый год создается новая папка. Поскольку дата в обратном порядке для каждого пользователя для каждого типа запроса обычно уникальна (никто не будет запрашивать одно и то же дважды в день), у нас не возникает проблем с поиском писем, если это необходимо. Эти письма генерируются системой, которая присваивает им уникальную тему, начинающуюся с [одобрено], за которой следует тип запроса.

Tman 10.04.2019 08:17

Стоит также упомянуть: нет необходимости автоматически определять эти письма в наших почтовых ящиках. Мы все еще можем сделать это вручную — мы знаем, какой запрос куда идет. Я просто хочу выбрать почту, нажать кнопку макроса на ленте и больше не беспокоиться об этом (правильное место сохранения / имя).

Tman 10.04.2019 08:21

Система отправляет утверждения для запроса пользователя через имя самого пользователя (отправляется как пользователь), поэтому .Sendername будет отличаться для каждого пользователя. Добавьте к этому дату ReceivedOn, которую мы сейчас используем, и имя файла наверняка будет уникальным. Для поиска этих писем используется внутренняя программа, поэтому имя файла должно соответствовать ранее упомянутому формату.

Tman 10.04.2019 08:34

Тем не менее, я мог бы добавить пользовательскую форму для отображения имени файла перед его сохранением в папку с возможностью изменить его, если оно неверно, просто чтобы убедиться, что все в порядке. На данный момент это просто то, что я хотел бы проверить, а не то, что я действительно начну использовать в производстве без тестирования и одобрения менеджера.

Tman 10.04.2019 08:36

В вашем вопросе есть три утверждения, которые строят savePath. Первый строит имя папки. Второй строит имя файла, кроме расширения. Третий добавляет расширение. Вам нужно заменить первые два оператора, чтобы создать нужные папки и имена файлов.

Tony Dallimore 10.04.2019 12:06

Если я правильно понимаю, тема содержит информацию, идентифицирующую тип запроса, который определяет имя папки назначения. У вас есть общая папка с таким именем, как «P:\EMEA Requests». Внутри у вас есть подпапки: «2019 xxxx», «2019 yyyy» и «2019 yyyy», где «xxxx», «yyyy» и «zzzz» определяют три типа запросов. Вы хотите, чтобы сообщения сохранялись в соответствующей подпапке в соответствии с типом запроса в теме. Это правильно? Каков формат этих предметов? Должно быть достаточно легко извлечь тип запроса из темы и сгенерировать путь.

Tony Dallimore 10.04.2019 12:07

Я так понимаю SenderName это что-то вроде "Джон Доу". Вы хотите, чтобы это изменилось на «John_Doe». Функция Replace сделает это.

Tony Dallimore 10.04.2019 12:07

Еще раз спасибо за помощь! Я действительно вижу, как это обретает форму сейчас. Вы правы в своих рассуждениях о типах запросов. к сожалению, в теме нет реального установленного значения, кроме [утверждено]. В какую папку ему нужно перейти, зависит от случая. К сожалению, компания не использует предустановленные шаблоны для формулирования этих запросов, поэтому тема будет такой, какая была зарегистрирована в системе первоначальным запросчиком.

Tman 10.04.2019 12:59

Из-за этого я хотел создать три отдельные кнопки или три параметра в макросе. Таким образом, мы можем просто выбрать тип запроса (аппаратное обеспечение, программная сеть или что-то в этом роде) и сохранить почту в правильной папке на основе этого ввода. Я надеюсь, что это все равно будет намного быстрее, чем сохранять почту в папке и переименовывать ее. Это также препятствует тому, чтобы люди случайно открыли неправильный путь и поместили их туда, где им не место.

Tman 10.04.2019 13:02

Я добавил второе предложение по созданию пути к моему второму ответу, который может обрабатывать несколько ключевых слов в теме.

Tony Dallimore 10.04.2019 14:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
22
657
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Это не прямой ответ на ваш вопрос. Это расследование, которое, я надеюсь, предоставит информацию, необходимую для ответа.

Вы говорите: «… письма автоматически генерируются системой…». Это может объяснить, почему я не совсем понимаю, почему ваш код работает. Я объясню свое замешательство после того, как предоставлю некоторую предысторию.

Существует четыре различных метода, с помощью которых можно выбрать MailItem для обработки:

  1. Пользователь может выбрать одно или несколько писем, а затем вызвать макрос для обработки выбранного MailItem. (Обратите внимание, что это электронное письмо пользователю, а MailItem макросу.)
  2. Макрос может читать вверх или вниз папку MailItems, просматривая свойства, чтобы определить, какие из них должны быть обработаны. Sort и Filter можно использовать для более быстрого нацеливания на интересующие MailItem.
  3. Вы можете указать правило, которое будет проверять каждое электронное письмо по мере его поступления и просматривать такие свойства, как тема и отправитель. Если письмо имеет необходимые свойства, можно выполнить ряд действий. Если стандартных действий недостаточно, вы можете связать макрос для выполнения любого действия, доступного для макроса VBA.
  4. Вы можете указать Outlook вызывать макрос всякий раз, когда происходит определенное событие. События включают в себя: MailItem добавлено в папку Xxxx, MailItem открыто, MailItem отправлено, MailItem сохранено, MailItem закрыто, MailItem ответило или MailItem переслано.

В вашем коде используется подход 4. В частности, вы используете событие MailItemsent. Вы говорите: «… мы получаем около сотни таких [электронных писем] в неделю…». Если «получить» — правильное слово, я ожидаю, что добавление MailItem в папку «Входящие» будет подходящим событием. Возможно, ваш код работает, потому что система генерирует электронные письма от пользователя X пользователю X.

Если эти электронные письма генерируются системой, мы не можем быть уверены, какие свойства установлены и какие значения для них установлены. Скопируйте приведенный ниже код в модуль Outlook. Выберите одно или несколько из этих писем и запустите макрос CallSubForSelectedEmails.

Option Explicit
Public Sub CallSubForSelectedEmails()

  Dim Exp As Explorer
  Dim ItemCrnt As MailItem

  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
  Else
    For Each ItemCrnt In Exp.Selection
      If ItemCrnt.Class = olMail Then
        Call DsplSimpleProperties(ItemCrnt)
      End If
    Next
  End If

End Sub
Sub DsplSimpleProperties(ItemCrnt As Outlook.MailItem)

  Dim InxR As Long

  Debug.Print "============================================= = "
  Debug.Print "  Profile: " & Session.CurrentProfileName
  Debug.Print "     User: " & Session.CurrentUser
  With ItemCrnt
    Debug.Print "  Created: " & .CreationTime
    Debug.Print " Receiver: " & .ReceivedByName
    Debug.Print " Received: " & .ReceivedTime
    For InxR = 1 To .Recipients.Count
      Debug.Print "Recipient: " & .Recipients(InxR)
    Next
    Debug.Print "   Sender: " & .Sender
    Debug.Print " SenderEA: " & .SenderEmailAddress
    Debug.Print " SenderNm: " & .SenderName
    Debug.Print "   SentOn: " & .SentOn
    Debug.Print "  Subject: " & .Subject
    Debug.Print "       To: " & .To
  End With

End Sub

Для одного из моих электронных писем эта процедура выводит:

==============================================
  Profile: Outlook
     User: Tony Dallimore
  Created: 08/04/2019 19:59:22
 Receiver: Tony Dallimore
 Received: 08/04/2019 18:45:39
Recipient: [email protected]
   Sender: Lifecake
 SenderEA: [email protected]
 SenderNm: Lifecake
   SentOn: 08/04/2019 18:45:37
  Subject: ?? Someone commented on Alex and Eric's video
       To: [email protected]

Примечание 1. Я одновременно являюсь пользователем системы и получателем этого электронного письма. Это дает два возможных способа получить мои имя и фамилию. Я использую инициалы в своем адресе электронной почты, но ваша компания может использовать имена.

Примечание 2: мой код использует подход 1 для выбора электронных писем для обработки. Макрос CallSubForSelectedEmails вызывает макрос DsplSimpleProperties для каждого выбранного письма. Я делаю все свои исследования и всю свою разработку макросов обработки электронной почты, используя такой код. Это дает мне полный контроль над тем, какие электронные письма обрабатываются. Профиль вызова для макроса DsplSimpleProperties такой же, как и для макроса правила или макроса события. После того, как я отладил свой макрос, используя подход 1, я переключился на его вызов из правила или события с минимальным дополнительным тестированием. Я не знаю более простого способа отладки макросов обработки электронной почты.

Спасибо за помощь. Я получаю вывод, аналогичный тому, который вы показываете здесь.

Tman 09.04.2019 09:31

Я буду дальше исследовать, как мы можем идентифицировать эти письма, и опубликую новый вопрос, как только у меня будет более четкое представление о них, а также о том, как создать сам макрос. Тем не менее большое спасибо за информацию!

Tman 09.04.2019 09:34

Небольшое обновление: используя ваш макрос для исследования писем, я обнаружил, что система отправляет письма, идентифицируя себя как самого пользователя. Это огромный шаг вперед, так как это делает .SenderName хорошим параметром для использования в будущем.

Tman 09.04.2019 09:50

Опять же, это не полный ответ, потому что у меня нет информации для полного ответа.

Задача 1: Создать путь

Информация для имени пути берется из MailItemSubject. Для этого примера я предполагаю, что тип запроса — 1, 2 или 3, и это последний символ темы.

Dim PathName As String

' Generate end of subfolder name
Select Case Right$(ItemCrnt.Subject,1)
  Case "1"
    PathName = "xxxx"
  Case "2"
    PathName = "yyyy"
  Case "3"
    PathName = "zzzz"
  Case Else
    ' Subject does not conform to expected format.
    Exit Sub
End Select  

' Prefix root folder name and year of subfolder name
PathName = "P:\EMEA Requests\" & Year(ItemCrnt.SentOn) & "\" & PathName

Right$ — это функция, которая извлекает указанное количество завершающих символов из строки. Также доступны функции Left$ и Mid$. Если тема достаточно сложная, мы можем рассмотреть Regex. Year — это функция, извлекающая год из даты. Значение будет целым числом, но VBA автоматически преобразует его в строку, если оно используется как строка.

Если подпрограмма не может определить тип запроса, она отбрасывает MailItem. Я обсужу этот вопрос позже.

Задание 1; Предложение 2: сгенерировать имя пути

Вы говорите, что темы не имеют фиксированного формата и просто включают слова из исходного запроса. Вы подразумеваете, что эти слова достаточно хороши, чтобы человек мог определить тип запроса. Таким образом, слова для запроса могут включать «аппаратное обеспечение», «h'ware», «компьютер» или «ноутбук». Другой запрос может включать «программное обеспечение», «приложение» или «приложение». Это простой метод обработки ситуации такого типа. Существует лучший метод, который я представлю, если он покажется возможным.

If Instr(1, LCase(ItemCrnt.Subject), "hardware") <> 0 Then 
  PathName = "xxxx"
ElseIf Instr(1, LCase(ItemCrnt.Subject), "h'ware") <> 0 Then 
  PathName = "xxxx"
ElseIf Instr(1, LCase(ItemCrnt.Subject), "computer") <> 0 Then 
  PathName = "xxxx"
ElseIf Instr(1, LCase(ItemCrnt.Subject), "laptop") <> 0 Then 
  PathName = "xxxx"
ElseIf Instr(1, LCase(ItemCrnt.Subject), "software") <> 0 Then 
  PathName = "yyyy"
ElseIf Instr(1, LCase(ItemCrnt.Subject), "application") <> 0 Then 
  PathName = "yyyy"
ElseIf Instr(1, LCase(ItemCrnt.Subject), "app") <> 0 Then 
  PathName = "yyyy"
Else
  PathName = ""
End If

Вы можете продолжать добавлять возможные ключевые слова, пока у ваших заказчиков не закончатся альтернативы. В противном случае вы можете использовать свою пользовательскую форму с подходом кнопок после того, как макрос обработает простые сообщения.

Задача 2: Создать имя файла

Dim FileName As String

FileName = Format(ItemCrnt.SentOn, "yymmdd") & " " & Replace(ItemCrnt.SenderName," ", "_")

Задача 0: Дизайн

Прежде чем начать кодирование, вам необходимо спроектировать весь процесс. Вы можете начать с чего-то простого, а затем развивать его по мере того, как лучше понимаете свои требования. Вы можете кодировать маленькие кусочки, как я сделал с PathName и FileName, чтобы вы могли понять биты, которые вам нужны, чтобы соответствовать друг другу. Но решение чего-то сложного без плана редко заканчивается удовлетворительно.

Мое понимание вашего требования неполное, но я попробую дизайн.

У меня было бы правило, согласно которому входящие электронные письма этого типа скопировано помещаются в папку Outlook, например «Несохраненные запросы EMEA». Примечание: это копии; оригинал остается в папке «Входящие» для обработки по мере необходимости. Я предполагаю, что есть способ идентифицировать эти электронные письма, который находится в пределах функциональности, доступной для правила.

У меня был бы весь код в макросе, который я вызывал бы один или два раза в день по мере необходимости. Этот макрос будет читать папку «Несохраненные запросы EMEA». Если он может сгенерировать путь и имя файла для сообщения, он сохранит сообщение в требуемой папке на диске и удалит сообщение из папки Outlook. Если он не мог обработать сообщение, он оставлял его в папке Outlook «Несохраненные запросы EMEA». Если сообщение осталось в папке Outlook «Несохраненные запросы EMEA», вы будете знать, что (1) макрос нуждается в доработке для обработки ранее неизвестного типа сообщения или (2) необходимо изменить правило, потому что оно скопировало сообщение неправильного типа.

Я сказал «прочитать папку», а не «прочитать папку». Вы получаете доступ к MailItem в папке по его положению: 1, 2, 3, … Folder.Count. Если вы удалите MailItem 2, то MailItem 3 станет MailItem 2, MailItem 4 станет MailItem 3 и так далее. Значение Folder.Count уменьшается на единицу. Иногда вы видите вопросы, спрашивающие, почему их макрос обрабатывает только все остальные MailItem. Причина в том, что у них есть кодировка вроде:

For InxI = 1 to Folder.Count
  ' Process and delete Folder.Item(InxI)
Next

С помощью приведенного выше кода вы по очереди обрабатываете элементы 1, 2, 3. Если вы удалите элемент 2, вы пропустите исходный элемент 3, потому что теперь он является элементом 2.

Правильный код:

For InxI = Folder.Count To 1 Step -1
  ' Process and delete Folder.Item(InxI)
Next

С помощью этого кода вы по очереди обрабатываете пункты 10, 9, 8, 7. Если вы удалите элемент 9, вам все равно, что элемент 10 стал элементом 9, потому что теперь вы обрабатываете элемент 8.

Если вы только читаете элементы, вам не нужно беспокоиться об этой проблеме. Но если вы добавляете или удаляете элементы, вам нужно об этом беспокоиться.

Другие вопросы по теме