PyEmailerAJM 1.3__tar.gz → 1.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyEmailerAJM
3
- Version: 1.3
3
+ Version: 1.5.1
4
4
  Summary: Allows for automating sending Email with the Outlook Desktop client. Future releases will add more client support
5
5
  Home-page: https://github.com/amcsparron2793-Water/PyEmailer
6
6
  Author: Amcsparron
7
7
  Author-email: amcsparron@albanyny.gov
8
8
  License: MIT License
9
- Download-URL: https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.3.tar.gz
9
+ Download-URL: https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.5.1.tar.gz
10
10
  Keywords: Outlook,Email,Automation
11
11
  Platform: UNKNOWN
12
12
  License-File: LICENSE.txt
@@ -4,7 +4,8 @@ PyEmailerAJM.py
4
4
 
5
5
  install win32 with pip install pywin32
6
6
  """
7
- from os.path import isfile, abspath, isabs, join
7
+ from os import environ
8
+ from os.path import isfile, abspath, isabs, join, isdir
8
9
 
9
10
  # imports
10
11
 
@@ -13,18 +14,28 @@ import win32com.client as win32
13
14
  # This is installed as part of pywin32
14
15
  from pythoncom import com_error
15
16
  from logging import Logger
17
+ from email_validator import validate_email, EmailNotValidError
16
18
 
17
19
 
18
20
  class EmailerNotSetupError(Exception):
19
21
  ...
20
22
 
21
23
 
24
+ class DisplayManualQuit(Exception):
25
+ ...
26
+
27
+
22
28
  class PyEmailer:
23
29
  # the email tab_char
24
30
  tab_char = ' '
31
+ signature_dir_path = join((environ['USERPROFILE']),
32
+ 'AppData\\Roaming\\Microsoft\\Signatures\\')
33
+
34
+ DisplayEmailSendTrackingWarning = "THIS TYPE OF SEND CANNOT BE DETECTED FOR SEND SUCCESS AUTOMATICALLY."
25
35
 
26
36
  def __init__(self, display_window: bool,
27
37
  send_emails: bool, logger: Logger = None,
38
+ email_sig_filename: str = None,
28
39
  auto_send: bool = False,
29
40
  email_app_name: str = 'outlook.application'):
30
41
 
@@ -40,6 +51,7 @@ class PyEmailer:
40
51
  self.auto_send = auto_send
41
52
  self.send_emails = send_emails
42
53
  self._setup_was_run = False
54
+ self._current_user_email = None
43
55
 
44
56
  self._recipient = None
45
57
  self._subject = None
@@ -47,16 +59,102 @@ class PyEmailer:
47
59
  self.read_folder = None
48
60
 
49
61
  try:
50
- self.email_app = win32.Dispatch(self.email_app_name)
51
- self._mapi_ns = self.email_app.GetNamespace('MAPI')
62
+ if self.email_app_name.lower().startswith('outlook'):
63
+ self.email_app = win32.Dispatch(self.email_app_name).GetNamespace('MAPI')
64
+ self._logger.debug("MAPI namespace in use.")
65
+ else:
66
+ self.email_app = win32.Dispatch(self.email_app_name)
52
67
  self.email = self.email_app.CreateItem(0)
53
68
  except com_error as e:
54
69
  self._logger.error(e, exc_info=True)
55
70
  raise e
56
71
 
72
+ self._email_signature = None
73
+ self._send_success = False
74
+ self.email_sig_filename = email_sig_filename
75
+
76
+ @property
77
+ def current_user_email(self):
78
+ if self.email_app_name.lower().startswith('outlook'):
79
+ self._current_user_email = (
80
+ self.email_app.Application.Session.CurrentUser.AddressEntry.GetExchangeUser().PrimarySmtpAddress)
81
+ return self._current_user_email
82
+
83
+ @current_user_email.setter
84
+ def current_user_email(self, value):
85
+ try:
86
+ if validate_email(value, check_deliverability=False):
87
+ self._current_user_email = value
88
+ except EmailNotValidError as e:
89
+ self._logger.error(e, exc_info=True)
90
+ value = None
91
+ self._current_user_email = value
92
+
93
+ @property
94
+ def email_signature(self):
95
+ return self._email_signature
96
+
97
+ @email_signature.getter
98
+ def email_signature(self):
99
+ if self.email_sig_filename:
100
+ signature_full_path = join(self.signature_dir_path, self.email_sig_filename)
101
+ if isdir(self.signature_dir_path):
102
+ pass
103
+ else:
104
+ try:
105
+ raise NotADirectoryError(f"{self.signature_dir_path} does not exist.")
106
+ except NotADirectoryError as e:
107
+ self._logger.warning(e)
108
+ self._email_signature = None
109
+
110
+ if isfile(signature_full_path):
111
+ with open(signature_full_path, 'r', encoding='utf-16') as f:
112
+ self._email_signature = f.read().strip()
113
+ else:
114
+ try:
115
+ raise FileNotFoundError(f"{signature_full_path} does not exist.")
116
+ except FileNotFoundError as e:
117
+ self._logger.warning(e)
118
+ self._email_signature = None
119
+ else:
120
+ self._email_signature = None
121
+
122
+ return self._email_signature
123
+
124
+ @property
125
+ def send_success(self):
126
+ return self._send_success
127
+
128
+ @send_success.setter
129
+ def send_success(self, value):
130
+ self._send_success = value
131
+
132
+ def _display_tracking_warning_confirm(self):
133
+ while True:
134
+ q = input(f"{self.DisplayEmailSendTrackingWarning}. Do you understand? (y/n): ").lower().strip()
135
+ if q == 'y':
136
+ self._logger.warning(self.DisplayEmailSendTrackingWarning)
137
+ return True
138
+ elif q == 'n':
139
+ return False
140
+ else:
141
+ print("Please respond with 'y' or 'n'.")
142
+
143
+ def display_tracker_check(self) -> bool:
144
+ if self.display_window:
145
+ c = self._display_tracking_warning_confirm()
146
+ if c:
147
+ return c
148
+ else:
149
+ try:
150
+ raise DisplayManualQuit("User cancelled operation due to DisplayTrackingWarning.")
151
+ except DisplayManualQuit as e:
152
+ self._logger.error(e, exc_info=True)
153
+ raise e
154
+
57
155
  def _GetReadFolder(self, email_dir_index: int = 6):
58
156
  # 6 = inbox
59
- self.read_folder = self._mapi_ns.GetDefaultFolder(email_dir_index)
157
+ self.read_folder = self.email_app.GetDefaultFolder(email_dir_index)
60
158
  return self.read_folder
61
159
 
62
160
  def GetMessages(self, folder_index=None):
@@ -90,20 +188,30 @@ class PyEmailer:
90
188
  reply_msg_match: bool = True) -> list:
91
189
  """Matches the message.Subject string to the subject attr string and returns a list of messages.
92
190
  If forward_message_match is True than messages are matched without
93
- regard to if they start with 'FW:' or 'FWD:'"""
191
+ regard to if they start with 'FW:' or 'FWD:'
192
+
193
+ If reply_msg_match is True than messages are matched without
194
+ regard to if they start with 'RE:'
195
+ """
94
196
  matched_messages = []
197
+ subject = subject.lower().strip()
198
+ fw_str = 'FW:'.lower()
199
+ fwd_str = 'FWD:'.lower()
200
+ re_str = 'RE:'.lower()
201
+
95
202
  for message in self.GetMessages():
203
+ message.Subject = message.Subject.lower()
96
204
  if forwarded_message_match:
97
205
  if (message.Subject == subject or
98
- (message.Subject.startswith('FW:')
99
- and message.Subject.split('FW:')[1].strip() == subject) or
100
- (message.Subject.startswith('FWD:')
101
- and message.Subject.split('FWD:')[1].strip() == subject)):
206
+ (message.Subject.startswith(fw_str)
207
+ and message.Subject.split(fw_str)[1].strip() == subject) or
208
+ (message.Subject.startswith(fwd_str)
209
+ and message.Subject.split(fwd_str)[1].strip() == subject)):
102
210
  matched_messages.append(message)
103
211
  if reply_msg_match:
104
212
  if (message.Subject == subject or
105
- (message.Subject.startswith('RE:')
106
- and message.Subject.split('RE:')[1].strip() == subject)):
213
+ (message.Subject.startswith(re_str)
214
+ and message.Subject.split(re_str)[1].strip() == subject)):
107
215
  matched_messages.append(message)
108
216
  else:
109
217
  if message.Subject == subject:
@@ -187,8 +295,10 @@ class PyEmailer:
187
295
 
188
296
  def _send(self):
189
297
  try:
298
+ self.send_success = False
190
299
  self.email.Send()
191
300
  # print(f"Mail sent to {self._recipient}")
301
+ self.send_success = True
192
302
  self._logger.info(f"Mail successfully sent to {self._recipient}")
193
303
  except Exception as e:
194
304
  self._logger.error(e, exc_info=True)
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyEmailerAJM
3
- Version: 1.3
3
+ Version: 1.5.1
4
4
  Summary: Allows for automating sending Email with the Outlook Desktop client. Future releases will add more client support
5
5
  Home-page: https://github.com/amcsparron2793-Water/PyEmailer
6
6
  Author: Amcsparron
7
7
  Author-email: amcsparron@albanyny.gov
8
8
  License: MIT License
9
- Download-URL: https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.3.tar.gz
9
+ Download-URL: https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.5.1.tar.gz
10
10
  Keywords: Outlook,Email,Automation
11
11
  Platform: UNKNOWN
12
12
  License-File: LICENSE.txt
@@ -0,0 +1,15 @@
1
+ # <u>PyEmailerAJM</u>
2
+ ### <i>Makes automating Outlook emails quick and easy</i>
3
+
4
+ - PyEmailerAJM uses pywin32 to automate the sending/reading of Outlook emails.
5
+
6
+ **Import**
7
+
8
+ - import PyEmailer class using `from PyEmailerAJM.PyEmailerAJM import PyEmailer`
9
+
10
+ **Notes**
11
+
12
+ - see [OutlookPywin32Commands.xlsx](OutlookPywin32Commands.xlsx) for list of commands that can be used with a message object.
13
+
14
+ - Email signature **text** can now be added as of v1.4
15
+ - Assuming the signature *.txt file is found in `%APPDATA%/Microsoft/Signatures/`
@@ -2,10 +2,10 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name='PyEmailerAJM',
5
- version='1.3',
5
+ version='1.5.1',
6
6
  packages=['PyEmailerAJM'],
7
7
  url='https://github.com/amcsparron2793-Water/PyEmailer',
8
- download_url='https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.3.tar.gz',
8
+ download_url='https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.5.1.tar.gz',
9
9
  keywords=["Outlook", "Email", "Automation"],
10
10
  install_requires=['pywin32'],
11
11
  license='MIT License',
@@ -1,10 +0,0 @@
1
- # <u>PyEmailerAJM</u>
2
- ### <i>Makes automating Outlook emails quick and easy</i>
3
-
4
- - PyEmailerAJM uses pywin32 to automate the sending/reading of Outlook emails.
5
-
6
- <b>Import</b>
7
-
8
- import PyEmailer class using `from PyEmailerAJM.PyEmailerAJM import PyEmailer`
9
-
10
- see [OutlookPywin32Commands.xlsx](OutlookPywin32Commands.xlsx) for list of commands that can be used with a message object.
File without changes
File without changes