exchangelib.items.message API documentation

# Module `exchangelib.items.message`

Expand source code

    import logging

    from ..fields import Base64Field, BooleanField, CharField, EWSElementField, MailboxField, MailboxListField, TextField
    from ..properties import ReferenceItemId, ReminderMessageData
    from ..util import require_account, require_id
    from ..version import EXCHANGE_2013, EXCHANGE_2013_SP1
    from .base import AUTO_RESOLVE, SEND_AND_SAVE_COPY, SEND_ONLY, SEND_TO_NONE, BaseReplyItem
    from .item import Item

    log = logging.getLogger(__name__)

    class Message(Item):
        """MSDN:
        https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/message-ex15websvcsotherref
        """

        ELEMENT_NAME = "Message"

        sender = MailboxField(field_uri="message:Sender", is_read_only=True, is_read_only_after_send=True)
        to_recipients = MailboxListField(
            field_uri="message:ToRecipients", is_read_only_after_send=True, is_searchable=False
        )
        cc_recipients = MailboxListField(
            field_uri="message:CcRecipients", is_read_only_after_send=True, is_searchable=False
        )
        bcc_recipients = MailboxListField(
            field_uri="message:BccRecipients", is_read_only_after_send=True, is_searchable=False
        )
        is_read_receipt_requested = BooleanField(
            field_uri="message:IsReadReceiptRequested", is_required=True, default=False, is_read_only_after_send=True
        )
        is_delivery_receipt_requested = BooleanField(
            field_uri="message:IsDeliveryReceiptRequested", is_required=True, default=False, is_read_only_after_send=True
        )
        conversation_index = Base64Field(field_uri="message:ConversationIndex", is_read_only=True)
        conversation_topic = CharField(field_uri="message:ConversationTopic", is_read_only=True)
        # Rename 'From' to 'author'. We can't use field name 'from' since it's a Python keyword.
        author = MailboxField(field_uri="message:From", is_read_only_after_send=True)
        message_id = TextField(field_uri="message:InternetMessageId", is_read_only_after_send=True)
        is_read = BooleanField(field_uri="message:IsRead", is_required=True, default=False)
        is_response_requested = BooleanField(field_uri="message:IsResponseRequested", default=False, is_required=True)
        references = TextField(field_uri="message:References")
        reply_to = MailboxListField(field_uri="message:ReplyTo", is_read_only_after_send=True, is_searchable=False)
        received_by = MailboxField(field_uri="message:ReceivedBy", is_read_only=True)
        received_representing = MailboxField(field_uri="message:ReceivedRepresenting", is_read_only=True)
        reminder_message_data = EWSElementField(
            field_uri="message:ReminderMessageData",
            value_cls=ReminderMessageData,
            supported_from=EXCHANGE_2013_SP1,
            is_read_only=True,
        )

        @require_account
        def send(
            self,
            save_copy=True,
            copy_to_folder=None,
            conflict_resolution=AUTO_RESOLVE,
            send_meeting_invitations=SEND_TO_NONE,
        ):
            from ..services import SendItem

            # Only sends a message. The message can either be an existing draft stored in EWS or a new message that does
            # not yet exist in EWS.
            if copy_to_folder and not save_copy:
                raise AttributeError("'save_copy' must be True when 'copy_to_folder' is set")
            if save_copy and not copy_to_folder:
                copy_to_folder = self.account.sent  # 'Sent' is default EWS behaviour
            if self.id:
                SendItem(account=self.account).get(items=[self], saved_item_folder=copy_to_folder)
                # The item will be deleted from the original folder
                self._id = None
                self.folder = copy_to_folder
                return None

            # New message
            if copy_to_folder:
                # This would better be done via send_and_save() but let's just support it here
                self.folder = copy_to_folder
                return self.send_and_save(
                    conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations
                )

            if self.account.version.build < EXCHANGE_2013 and self.attachments:
                # At least some versions prior to Exchange 2013 can't send attachments immediately. You need to first save,
                # then attach, then send. This is done in send_and_save(). send() will delete the item again.
                self.send_and_save(
                    conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations
                )
                return None

            self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations)
            return None

        def send_and_save(
            self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE
        ):
            # Sends Message and saves a copy in the parent folder. Does not return an ItemId.
            if self.id:
                self._update(
                    update_fieldnames=update_fields,
                    message_disposition=SEND_AND_SAVE_COPY,
                    conflict_resolution=conflict_resolution,
                    send_meeting_invitations=send_meeting_invitations,
                )
            else:
                if self.account.version.build < EXCHANGE_2013 and self.attachments:
                    # At least some versions prior to Exchange 2013 can't send-and-save attachments immediately. You need
                    # to first save, then attach, then send. This is done in save().
                    self.save(
                        update_fields=update_fields,
                        conflict_resolution=conflict_resolution,
                        send_meeting_invitations=send_meeting_invitations,
                    )
                    self.send(
                        save_copy=False,
                        conflict_resolution=conflict_resolution,
                        send_meeting_invitations=send_meeting_invitations,
                    )
                else:
                    self._create(message_disposition=SEND_AND_SAVE_COPY, send_meeting_invitations=send_meeting_invitations)

        @require_id
        def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None):
            if to_recipients is None:
                if not self.author:
                    raise ValueError("'to_recipients' must be set when message has no 'author'")
                to_recipients = [self.author]
            return ReplyToItem(
                account=self.account,
                reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey),
                subject=subject,
                new_body=body,
                to_recipients=to_recipients,
                cc_recipients=cc_recipients,
                bcc_recipients=bcc_recipients,
            )

        def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None):
            self.create_reply(subject, body, to_recipients, cc_recipients, bcc_recipients).send()

        @require_id
        def create_reply_all(self, subject, body):
            to_recipients = list(self.to_recipients) if self.to_recipients else []
            if self.author:
                to_recipients.append(self.author)
            return ReplyAllToItem(
                account=self.account,
                reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey),
                subject=subject,
                new_body=body,
                to_recipients=to_recipients,
                cc_recipients=self.cc_recipients,
                bcc_recipients=self.bcc_recipients,
            )

        def reply_all(self, subject, body):
            self.create_reply_all(subject, body).send()

        def mark_as_junk(self, is_junk=True, move_item=True):
            """Mark or un-marks items as junk email.

            :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be
            removed.
            :param move_item: If true, the item will be moved to the junk folder.
            :return:
            """
            from ..services import MarkAsJunk

            res = MarkAsJunk(account=self.account).get(
                items=[self], is_junk=is_junk, move_item=move_item, expect_result=None
            )
            if res is None:
                return
            self.folder = self.account.junk if is_junk else self.account.inbox
            self.id, self.changekey = res

    class ReplyToItem(BaseReplyItem):
        """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replytoitem"""

        ELEMENT_NAME = "ReplyToItem"

    class ReplyAllToItem(BaseReplyItem):
        """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replyalltoitem"""

        ELEMENT_NAME = "ReplyAllToItem"

    class ForwardItem(BaseReplyItem):
        """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/forwarditem"""

        ELEMENT_NAME = "ForwardItem"

## Classes

`class ForwardItem (**kwargs)`

  MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/forwarditem

  Expand source code

      class ForwardItem(BaseReplyItem):
          """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/forwarditem"""

          ELEMENT_NAME = "ForwardItem"

  ### Ancestors

  - BaseReplyItem
  - EWSElement

  ### Class variables

  `var ELEMENT_NAME`

  ### Inherited members

  - `BaseReplyItem`:
    - `account`
    - `add_field`
    - `remove_field`
    - `save`
    - `supported_fields`
    - `validate_field`
`class Message (**kwargs)`

  MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/message-ex15websvcsotherref

  Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.

  :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.

  Expand source code

      class Message(Item):
          """MSDN:
          https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/message-ex15websvcsotherref
          """

          ELEMENT_NAME = "Message"

          sender = MailboxField(field_uri="message:Sender", is_read_only=True, is_read_only_after_send=True)
          to_recipients = MailboxListField(
              field_uri="message:ToRecipients", is_read_only_after_send=True, is_searchable=False
          )
          cc_recipients = MailboxListField(
              field_uri="message:CcRecipients", is_read_only_after_send=True, is_searchable=False
          )
          bcc_recipients = MailboxListField(
              field_uri="message:BccRecipients", is_read_only_after_send=True, is_searchable=False
          )
          is_read_receipt_requested = BooleanField(
              field_uri="message:IsReadReceiptRequested", is_required=True, default=False, is_read_only_after_send=True
          )
          is_delivery_receipt_requested = BooleanField(
              field_uri="message:IsDeliveryReceiptRequested", is_required=True, default=False, is_read_only_after_send=True
          )
          conversation_index = Base64Field(field_uri="message:ConversationIndex", is_read_only=True)
          conversation_topic = CharField(field_uri="message:ConversationTopic", is_read_only=True)
          # Rename 'From' to 'author'. We can't use field name 'from' since it's a Python keyword.
          author = MailboxField(field_uri="message:From", is_read_only_after_send=True)
          message_id = TextField(field_uri="message:InternetMessageId", is_read_only_after_send=True)
          is_read = BooleanField(field_uri="message:IsRead", is_required=True, default=False)
          is_response_requested = BooleanField(field_uri="message:IsResponseRequested", default=False, is_required=True)
          references = TextField(field_uri="message:References")
          reply_to = MailboxListField(field_uri="message:ReplyTo", is_read_only_after_send=True, is_searchable=False)
          received_by = MailboxField(field_uri="message:ReceivedBy", is_read_only=True)
          received_representing = MailboxField(field_uri="message:ReceivedRepresenting", is_read_only=True)
          reminder_message_data = EWSElementField(
              field_uri="message:ReminderMessageData",
              value_cls=ReminderMessageData,
              supported_from=EXCHANGE_2013_SP1,
              is_read_only=True,
          )

          @require_account
          def send(
              self,
              save_copy=True,
              copy_to_folder=None,
              conflict_resolution=AUTO_RESOLVE,
              send_meeting_invitations=SEND_TO_NONE,
          ):
              from ..services import SendItem

              # Only sends a message. The message can either be an existing draft stored in EWS or a new message that does
              # not yet exist in EWS.
              if copy_to_folder and not save_copy:
                  raise AttributeError("'save_copy' must be True when 'copy_to_folder' is set")
              if save_copy and not copy_to_folder:
                  copy_to_folder = self.account.sent  # 'Sent' is default EWS behaviour
              if self.id:
                  SendItem(account=self.account).get(items=[self], saved_item_folder=copy_to_folder)
                  # The item will be deleted from the original folder
                  self._id = None
                  self.folder = copy_to_folder
                  return None

              # New message
              if copy_to_folder:
                  # This would better be done via send_and_save() but let's just support it here
                  self.folder = copy_to_folder
                  return self.send_and_save(
                      conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations
                  )

              if self.account.version.build < EXCHANGE_2013 and self.attachments:
                  # At least some versions prior to Exchange 2013 can't send attachments immediately. You need to first save,
                  # then attach, then send. This is done in send_and_save(). send() will delete the item again.
                  self.send_and_save(
                      conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations
                  )
                  return None

              self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations)
              return None

          def send_and_save(
              self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE
          ):
              # Sends Message and saves a copy in the parent folder. Does not return an ItemId.
              if self.id:
                  self._update(
                      update_fieldnames=update_fields,
                      message_disposition=SEND_AND_SAVE_COPY,
                      conflict_resolution=conflict_resolution,
                      send_meeting_invitations=send_meeting_invitations,
                  )
              else:
                  if self.account.version.build < EXCHANGE_2013 and self.attachments:
                      # At least some versions prior to Exchange 2013 can't send-and-save attachments immediately. You need
                      # to first save, then attach, then send. This is done in save().
                      self.save(
                          update_fields=update_fields,
                          conflict_resolution=conflict_resolution,
                          send_meeting_invitations=send_meeting_invitations,
                      )
                      self.send(
                          save_copy=False,
                          conflict_resolution=conflict_resolution,
                          send_meeting_invitations=send_meeting_invitations,
                      )
                  else:
                      self._create(message_disposition=SEND_AND_SAVE_COPY, send_meeting_invitations=send_meeting_invitations)

          @require_id
          def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None):
              if to_recipients is None:
                  if not self.author:
                      raise ValueError("'to_recipients' must be set when message has no 'author'")
                  to_recipients = [self.author]
              return ReplyToItem(
                  account=self.account,
                  reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey),
                  subject=subject,
                  new_body=body,
                  to_recipients=to_recipients,
                  cc_recipients=cc_recipients,
                  bcc_recipients=bcc_recipients,
              )

          def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None):
              self.create_reply(subject, body, to_recipients, cc_recipients, bcc_recipients).send()

          @require_id
          def create_reply_all(self, subject, body):
              to_recipients = list(self.to_recipients) if self.to_recipients else []
              if self.author:
                  to_recipients.append(self.author)
              return ReplyAllToItem(
                  account=self.account,
                  reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey),
                  subject=subject,
                  new_body=body,
                  to_recipients=to_recipients,
                  cc_recipients=self.cc_recipients,
                  bcc_recipients=self.bcc_recipients,
              )

          def reply_all(self, subject, body):
              self.create_reply_all(subject, body).send()

          def mark_as_junk(self, is_junk=True, move_item=True):
              """Mark or un-marks items as junk email.

              :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be
              removed.
              :param move_item: If true, the item will be moved to the junk folder.
              :return:
              """
              from ..services import MarkAsJunk

              res = MarkAsJunk(account=self.account).get(
                  items=[self], is_junk=is_junk, move_item=move_item, expect_result=None
              )
              if res is None:
                  return
              self.folder = self.account.junk if is_junk else self.account.inbox
              self.id, self.changekey = res

  ### Ancestors

  - Item
  - BaseItem
  - RegisterMixIn
  - IdChangeKeyMixIn
  - EWSElement

  ### Class variables

  `var ELEMENT_NAME`
  `var FIELDS`

  ### Instance variables

  `var author`
  `var bcc_recipients`
  `var cc_recipients`
  `var conversation_index`
  `var conversation_topic`
  `var is_delivery_receipt_requested`
  `var is_read`
  `var is_read_receipt_requested`
  `var is_response_requested`
  `var message_id`
  `var received_by`
  `var received_representing`
  `var references`
  `var reminder_message_data`
  `var reply_to`
  `var sender`
  `var to_recipients`

  ### Methods

  `def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None)`
    Expand source code

        @require_id
        def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None):
            if to_recipients is None:
                if not self.author:
                    raise ValueError("'to_recipients' must be set when message has no 'author'")
                to_recipients = [self.author]
            return ReplyToItem(
                account=self.account,
                reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey),
                subject=subject,
                new_body=body,
                to_recipients=to_recipients,
                cc_recipients=cc_recipients,
                bcc_recipients=bcc_recipients,
            )

  `def create_reply_all(self, subject, body)`
    Expand source code

        @require_id
        def create_reply_all(self, subject, body):
            to_recipients = list(self.to_recipients) if self.to_recipients else []
            if self.author:
                to_recipients.append(self.author)
            return ReplyAllToItem(
                account=self.account,
                reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey),
                subject=subject,
                new_body=body,
                to_recipients=to_recipients,
                cc_recipients=self.cc_recipients,
                bcc_recipients=self.bcc_recipients,
            )

  `def mark_as_junk(self, is_junk=True, move_item=True)`

    Mark or un-marks items as junk email.

    :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be removed. :param move_item: If true, the item will be moved to the junk folder. :return:

    Expand source code

        def mark_as_junk(self, is_junk=True, move_item=True):
            """Mark or un-marks items as junk email.

            :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be
            removed.
            :param move_item: If true, the item will be moved to the junk folder.
            :return:
            """
            from ..services import MarkAsJunk

            res = MarkAsJunk(account=self.account).get(
                items=[self], is_junk=is_junk, move_item=move_item, expect_result=None
            )
            if res is None:
                return
            self.folder = self.account.junk if is_junk else self.account.inbox
            self.id, self.changekey = res

  `def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None)`
    Expand source code

        def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None):
            self.create_reply(subject, body, to_recipients, cc_recipients, bcc_recipients).send()

  `def reply_all(self, subject, body)`
    Expand source code

        def reply_all(self, subject, body):
            self.create_reply_all(subject, body).send()

  `def send(self, save_copy=True, copy_to_folder=None, conflict_resolution='AutoResolve', send_meeting_invitations='SendToNone')`
    Expand source code

        @require_account
        def send(
            self,
            save_copy=True,
            copy_to_folder=None,
            conflict_resolution=AUTO_RESOLVE,
            send_meeting_invitations=SEND_TO_NONE,
        ):
            from ..services import SendItem

            # Only sends a message. The message can either be an existing draft stored in EWS or a new message that does
            # not yet exist in EWS.
            if copy_to_folder and not save_copy:
                raise AttributeError("'save_copy' must be True when 'copy_to_folder' is set")
            if save_copy and not copy_to_folder:
                copy_to_folder = self.account.sent  # 'Sent' is default EWS behaviour
            if self.id:
                SendItem(account=self.account).get(items=[self], saved_item_folder=copy_to_folder)
                # The item will be deleted from the original folder
                self._id = None
                self.folder = copy_to_folder
                return None

            # New message
            if copy_to_folder:
                # This would better be done via send_and_save() but let's just support it here
                self.folder = copy_to_folder
                return self.send_and_save(
                    conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations
                )

            if self.account.version.build < EXCHANGE_2013 and self.attachments:
                # At least some versions prior to Exchange 2013 can't send attachments immediately. You need to first save,
                # then attach, then send. This is done in send_and_save(). send() will delete the item again.
                self.send_and_save(
                    conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations
                )
                return None

            self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations)
            return None

  `def send_and_save(self, update_fields=None, conflict_resolution='AutoResolve', send_meeting_invitations='SendToNone')`
    Expand source code

        def send_and_save(
            self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE
        ):
            # Sends Message and saves a copy in the parent folder. Does not return an ItemId.
            if self.id:
                self._update(
                    update_fieldnames=update_fields,
                    message_disposition=SEND_AND_SAVE_COPY,
                    conflict_resolution=conflict_resolution,
                    send_meeting_invitations=send_meeting_invitations,
                )
            else:
                if self.account.version.build < EXCHANGE_2013 and self.attachments:
                    # At least some versions prior to Exchange 2013 can't send-and-save attachments immediately. You need
                    # to first save, then attach, then send. This is done in save().
                    self.save(
                        update_fields=update_fields,
                        conflict_resolution=conflict_resolution,
                        send_meeting_invitations=send_meeting_invitations,
                    )
                    self.send(
                        save_copy=False,
                        conflict_resolution=conflict_resolution,
                        send_meeting_invitations=send_meeting_invitations,
                    )
                else:
                    self._create(message_disposition=SEND_AND_SAVE_COPY, send_meeting_invitations=send_meeting_invitations)

  ### Inherited members

  - `Item`:
    - `ID_ELEMENT_CLS`
    - `account`
    - `add_field`
    - `attach`
    - `deregister`
    - `detach`
    - `folder`
    - `register`
    - `remove_field`
    - `supported_fields`
    - `validate_field`
`class ReplyAllToItem (**kwargs)`

  MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replyalltoitem

  Expand source code

      class ReplyAllToItem(BaseReplyItem):
          """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replyalltoitem"""

          ELEMENT_NAME = "ReplyAllToItem"

  ### Ancestors

  - BaseReplyItem
  - EWSElement

  ### Class variables

  `var ELEMENT_NAME`

  ### Inherited members

  - `BaseReplyItem`:
    - `account`
    - `add_field`
    - `remove_field`
    - `save`
    - `supported_fields`
    - `validate_field`
`class ReplyToItem (**kwargs)`

  MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replytoitem

  Expand source code

      class ReplyToItem(BaseReplyItem):
          """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replytoitem"""

          ELEMENT_NAME = "ReplyToItem"

  ### Ancestors

  - BaseReplyItem
  - EWSElement

  ### Class variables

  `var ELEMENT_NAME`

  ### Inherited members

  - `BaseReplyItem`:
    - `account`
    - `add_field`
    - `remove_field`
    - `save`
    - `supported_fields`
    - `validate_field`