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.
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PKG-INFO +2 -2
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PyEmailerAJM/PyEmailerAJM.py +121 -11
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PyEmailerAJM.egg-info/PKG-INFO +2 -2
- PyEmailerAJM-1.5.1/README.md +15 -0
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/setup.py +2 -2
- PyEmailerAJM-1.3/README.md +0 -10
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/LICENSE.txt +0 -0
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PyEmailerAJM/__init__.py +0 -0
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PyEmailerAJM.egg-info/SOURCES.txt +0 -0
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PyEmailerAJM.egg-info/dependency_links.txt +0 -0
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PyEmailerAJM.egg-info/requires.txt +0 -0
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/PyEmailerAJM.egg-info/top_level.txt +0 -0
- {PyEmailerAJM-1.3 → PyEmailerAJM-1.5.1}/setup.cfg +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyEmailerAJM
|
|
3
|
-
Version: 1.
|
|
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.
|
|
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
|
|
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.
|
|
51
|
-
|
|
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.
|
|
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(
|
|
99
|
-
and message.Subject.split(
|
|
100
|
-
(message.Subject.startswith(
|
|
101
|
-
and message.Subject.split(
|
|
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(
|
|
106
|
-
and message.Subject.split(
|
|
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
|
+
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.
|
|
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.
|
|
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.
|
|
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',
|
PyEmailerAJM-1.3/README.md
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|