PyEmailerAJM 0.1__tar.gz → 1.3__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: 0.1
3
+ Version: 1.3
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/0.1.tar.gz
9
+ Download-URL: https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.3.tar.gz
10
10
  Keywords: Outlook,Email,Automation
11
11
  Platform: UNKNOWN
12
12
  License-File: LICENSE.txt
@@ -0,0 +1,262 @@
1
+ #! python3
2
+ """
3
+ PyEmailerAJM.py
4
+
5
+ install win32 with pip install pywin32
6
+ """
7
+ from os.path import isfile, abspath, isabs, join
8
+
9
+ # imports
10
+
11
+ # install win32 with pip install pywin32
12
+ import win32com.client as win32
13
+ # This is installed as part of pywin32
14
+ from pythoncom import com_error
15
+ from logging import Logger
16
+
17
+
18
+ class EmailerNotSetupError(Exception):
19
+ ...
20
+
21
+
22
+ class PyEmailer:
23
+ # the email tab_char
24
+ tab_char = ' '
25
+
26
+ def __init__(self, display_window: bool,
27
+ send_emails: bool, logger: Logger = None,
28
+ auto_send: bool = False,
29
+ email_app_name: str = 'outlook.application'):
30
+
31
+ if logger:
32
+ self._logger = logger
33
+ else:
34
+ self._logger = Logger("DUMMY")
35
+ # print("Dummy logger in use!")
36
+
37
+ self.email_app_name = email_app_name
38
+
39
+ self.display_window = display_window
40
+ self.auto_send = auto_send
41
+ self.send_emails = send_emails
42
+ self._setup_was_run = False
43
+
44
+ self._recipient = None
45
+ self._subject = None
46
+ self._text = None
47
+ self.read_folder = None
48
+
49
+ try:
50
+ self.email_app = win32.Dispatch(self.email_app_name)
51
+ self._mapi_ns = self.email_app.GetNamespace('MAPI')
52
+ self.email = self.email_app.CreateItem(0)
53
+ except com_error as e:
54
+ self._logger.error(e, exc_info=True)
55
+ raise e
56
+
57
+ def _GetReadFolder(self, email_dir_index: int = 6):
58
+ # 6 = inbox
59
+ self.read_folder = self._mapi_ns.GetDefaultFolder(email_dir_index)
60
+ return self.read_folder
61
+
62
+ def GetMessages(self, folder_index=None):
63
+ if isinstance(folder_index, int):
64
+ self.read_folder = self._GetReadFolder(folder_index)
65
+ elif not folder_index and self.read_folder:
66
+ pass
67
+ elif not folder_index:
68
+ self.read_folder = self._GetReadFolder()
69
+ else:
70
+ try:
71
+ raise TypeError("folder_index must be an integer or self.read_folder must be defined")
72
+ except TypeError as e:
73
+ self._logger.error(e, exc_info=True)
74
+ raise e
75
+ return self.read_folder.Items
76
+
77
+ def GetEmailMessageBody(self, msg):
78
+ """message = messages.GetLast()"""
79
+ body_content = msg.body
80
+ if body_content:
81
+ return body_content
82
+ else:
83
+ try:
84
+ raise ValueError("This message has no body.")
85
+ except ValueError as e:
86
+ self._logger.error(e, exc_info=True)
87
+ raise e
88
+
89
+ def FindMsgBySubject(self, subject: str, forwarded_message_match: bool = True,
90
+ reply_msg_match: bool = True) -> list:
91
+ """Matches the message.Subject string to the subject attr string and returns a list of messages.
92
+ If forward_message_match is True than messages are matched without
93
+ regard to if they start with 'FW:' or 'FWD:'"""
94
+ matched_messages = []
95
+ for message in self.GetMessages():
96
+ if forwarded_message_match:
97
+ 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)):
102
+ matched_messages.append(message)
103
+ if reply_msg_match:
104
+ if (message.Subject == subject or
105
+ (message.Subject.startswith('RE:')
106
+ and message.Subject.split('RE:')[1].strip() == subject)):
107
+ matched_messages.append(message)
108
+ else:
109
+ if message.Subject == subject:
110
+ matched_messages.append(message)
111
+
112
+ return matched_messages
113
+
114
+ def SaveAllEmailAttachments(self, msg, save_dir_path):
115
+ attachments = msg.Attachments
116
+ for attachment in attachments:
117
+ full_save_path = join(save_dir_path, str(attachment))
118
+ try:
119
+ attachment.SaveAsFile(full_save_path)
120
+ self._logger.debug(f"{full_save_path} saved from email with subject {msg.subject}")
121
+ except Exception as e:
122
+ self._logger.error(e, exc_info=True)
123
+ raise e
124
+
125
+ def SetupEmail(self, recipient: str, subject: str, text: str, attachments: list = None):
126
+ def _validate_attachments():
127
+ if attachments:
128
+ if isinstance(attachments, list):
129
+ for a in attachments:
130
+ if isfile(a):
131
+ if isabs(a):
132
+ self.email.Attachments.Add(a)
133
+ else:
134
+ a = abspath(a)
135
+ if isfile(a):
136
+ self.email.Attachments.Add(a)
137
+ else:
138
+ try:
139
+ raise FileNotFoundError(f"file {a} could not be attached.")
140
+ except FileNotFoundError as e:
141
+ self._logger.error(e, exc_info=True)
142
+ raise e
143
+ else:
144
+ try:
145
+ raise FileNotFoundError(f"file {a} could not be attached.")
146
+ except FileNotFoundError as e:
147
+ self._logger.error(e, exc_info=True)
148
+ raise e
149
+ else:
150
+ try:
151
+ raise TypeError("attachments attribute must be a list")
152
+ except TypeError as e:
153
+ self._logger.error(e, exc_info=True)
154
+ raise e
155
+ else:
156
+ self._logger.debug("no attachments detected")
157
+
158
+ try:
159
+ # set the params
160
+ _validate_attachments()
161
+ self.email.To = recipient
162
+ self.email.Subject = subject
163
+ self.email.HtmlBody = text
164
+
165
+ self._recipient = self.email.To
166
+ self._subject = self.email.Subject
167
+ self._text = self.email.HtmlBody
168
+
169
+ # print("New email set up successfully.")
170
+ self._logger.info("New email set up successfully. see debug for details")
171
+ self._logger.debug(f"Email recipient {recipient}, Subject {subject}, Message body {text}")
172
+ self._setup_was_run = True
173
+ return self.email
174
+
175
+ except Exception as e:
176
+ self._logger.error(e, exc_info=True)
177
+ raise e
178
+
179
+ def _display(self):
180
+ # print(f"Displaying the email in {self.email_app_name}, this window might open minimized.")
181
+ self._logger.info(f"Displaying the email in {self.email_app_name}, this window might open minimized.")
182
+ try:
183
+ self.email.Display(True)
184
+ except Exception as e:
185
+ self._logger.error(e, exc_info=True)
186
+ raise e
187
+
188
+ def _send(self):
189
+ try:
190
+ self.email.Send()
191
+ # print(f"Mail sent to {self._recipient}")
192
+ self._logger.info(f"Mail successfully sent to {self._recipient}")
193
+ except Exception as e:
194
+ self._logger.error(e, exc_info=True)
195
+ raise e
196
+
197
+ def _manual_send_loop(self):
198
+ while True:
199
+ yn = input("Send Mail? (y/n/q): ").lower()
200
+ if yn == 'y':
201
+ self._send()
202
+ break
203
+ elif yn == 'n':
204
+ self._logger.info(f"Mail not sent to {self._recipient}")
205
+ print(f"Mail not sent to {self._recipient}")
206
+ break
207
+ elif yn == 'q':
208
+ print("ok quitting!")
209
+ self._logger.warning("Quitting early due to user input.")
210
+ exit(-1)
211
+ else:
212
+ print("Please choose \'y\', \'n\' or \'q\'")
213
+
214
+ def SendOrDisplay(self):
215
+ if self._setup_was_run:
216
+ # print(f"Ready to send/display mail to/for {self._recipient}...")
217
+ self._logger.info(f"Ready to send/display mail to/for {self._recipient}...")
218
+ if self.send_emails and self.display_window:
219
+ send_and_display_warning = ("Sending email while also displaying the email "
220
+ "in the app is not possible. Defaulting to Display only")
221
+ # print(send_and_display_warning)
222
+ self._logger.warning(send_and_display_warning)
223
+ self.send_emails = False
224
+ self.display_window = True
225
+
226
+ if self.send_emails:
227
+ if self.auto_send:
228
+ self._logger.info("Sending emails with auto_send...")
229
+ self._send()
230
+ else:
231
+ self._manual_send_loop()
232
+
233
+ elif self.display_window:
234
+ self._display()
235
+ else:
236
+ both_disabled_warning = ("Both sending and displaying the email are disabled. "
237
+ "No errors were encountered.")
238
+ self._logger.warning(both_disabled_warning)
239
+ # print(both_disabled_warning)
240
+ else:
241
+ try:
242
+ raise EmailerNotSetupError("Setup has not been run, sending or displaying an email cannot occur.")
243
+ except EmailerNotSetupError as e:
244
+ self._logger.error(e, exc_info=True)
245
+ raise e
246
+
247
+
248
+ if __name__ == "__main__":
249
+ module_name = __file__.split('\\')[-1].split('.py')[0]
250
+
251
+ """emailer = PyEmailer(display_window=False, send_emails=True, auto_send=False)
252
+
253
+ r_dict = {
254
+ "subject": f"TEST: Your TEST "
255
+ f"agreement expires in 30 days or less!",
256
+ "text": "testing to see if the attachment works",
257
+ "recipient": 'test',
258
+ "attachments": []
259
+ }
260
+ #   is the tab character for emails
261
+ emailer.SetupEmail(**r_dict) # recipient="test", subject="test subject", text="test_body")
262
+ emailer.SendOrDisplay()"""
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyEmailerAJM
3
- Version: 0.1
3
+ Version: 1.3
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/0.1.tar.gz
9
+ Download-URL: https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.3.tar.gz
10
10
  Keywords: Outlook,Email,Automation
11
11
  Platform: UNKNOWN
12
12
  License-File: LICENSE.txt
@@ -0,0 +1,10 @@
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.
@@ -2,10 +2,10 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name='PyEmailerAJM',
5
- version='0.1',
5
+ version='1.3',
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/0.1.tar.gz',
8
+ download_url='https://github.com/amcsparron2793-Water/PyEmailer/archive/refs/tags/1.3.tar.gz',
9
9
  keywords=["Outlook", "Email", "Automation"],
10
10
  install_requires=['pywin32'],
11
11
  license='MIT License',
@@ -1,164 +0,0 @@
1
- #! python3
2
- """
3
- PyEmailerAJM.py
4
-
5
- install win32 with pip install pywin32
6
- """
7
-
8
- # imports
9
-
10
- # install win32 with pip install pywin32
11
- import win32com.client as win32
12
- # This is installed as part of pywin32
13
- from pythoncom import com_error
14
- from logging import Logger
15
-
16
-
17
- class EmailerNotSetupError(Exception):
18
- ...
19
-
20
-
21
- class PyEmailer:
22
- def __init__(self, display_window: bool,
23
- send_emails: bool, logger: Logger = None,
24
- auto_send: bool = False,
25
- email_app_name: str = 'outlook.application'):
26
-
27
- if logger:
28
- self._logger = logger
29
- else:
30
- self._logger = Logger("DUMMY")
31
- # print("Dummy logger in use!")
32
-
33
- self.email_app_name = email_app_name
34
-
35
- self.display_window = display_window
36
- self.auto_send = auto_send
37
- self.send_emails = send_emails
38
- self._setup_was_run = False
39
-
40
- self._recipient = None
41
- self._subject = None
42
- self._text = None
43
-
44
- try:
45
- self.email_app = win32.Dispatch(self.email_app_name)
46
- self.email = self.email_app.CreateItem(0)
47
- except com_error as e:
48
- self._logger.error(e, exc_info=True)
49
- raise e
50
-
51
- def SetupEmail(self, recipient: str, subject: str, text: str):
52
- try:
53
- # set the params
54
- self.email.To = recipient
55
- self.email.Subject = subject
56
- self.email.HtmlBody = text
57
-
58
- self._recipient = self.email.To
59
- self._subject = self.email.Subject
60
- self._text = self.email.HtmlBody
61
-
62
- # print("New email set up successfully.")
63
- self._logger.info("New email set up successfully. see debug for details")
64
- self._logger.debug(f"Email recipient {recipient}, Subject {subject}, Message body {text}")
65
- self._setup_was_run = True
66
- return self.email
67
-
68
- except Exception as e:
69
- self._logger.error(e, exc_info=True)
70
- raise e
71
-
72
- def _display(self):
73
- # print(f"Displaying the email in {self.email_app_name}, this window might open minimized.")
74
- self._logger.info(f"Displaying the email in {self.email_app_name}, this window might open minimized.")
75
- try:
76
- self.email.Display(True)
77
- except Exception as e:
78
- self._logger.error(e, exc_info=True)
79
- raise e
80
-
81
- def _send(self):
82
- try:
83
- self.email.Send()
84
- # print(f"Mail sent to {self._recipient}")
85
- self._logger.info(f"Mail successfully sent to {self._recipient}")
86
- except Exception as e:
87
- self._logger.error(e, exc_info=True)
88
- raise e
89
-
90
- def _manual_send_loop(self):
91
- while True:
92
- yn = input("Send Mail? (y/n/q): ").lower()
93
- if yn == 'y':
94
- self._send()
95
- break
96
- elif yn == 'n':
97
- self._logger.info(f"Mail not sent to {self._recipient}")
98
- print(f"Mail not sent to {self._recipient}")
99
- break
100
- elif yn == 'q':
101
- print("ok quitting!")
102
- self._logger.warning("Quitting early due to user input.")
103
- exit(-1)
104
- else:
105
- print("Please choose \'y\', \'n\' or \'q\'")
106
-
107
- def SendOrDisplay(self):
108
- if self._setup_was_run:
109
- # print(f"Ready to send/display mail to/for {self._recipient}...")
110
- self._logger.info(f"Ready to send/display mail to/for {self._recipient}...")
111
- if self.send_emails and self.display_window:
112
- send_and_display_warning = ("Sending email while also displaying the email "
113
- "in the app is not possible. Defaulting to Display only")
114
- # print(send_and_display_warning)
115
- self._logger.warning(send_and_display_warning)
116
- self.send_emails = False
117
- self.display_window = True
118
-
119
- if self.send_emails:
120
- if self.auto_send:
121
- self._logger.info("Sending emails with auto_send...")
122
- self._send()
123
- else:
124
- self._manual_send_loop()
125
-
126
- elif self.display_window:
127
- self._display()
128
- else:
129
- both_disabled_warning = ("Both sending and displaying the email are disabled. "
130
- "No errors were encountered.")
131
- self._logger.warning(both_disabled_warning)
132
- # print(both_disabled_warning)
133
- else:
134
- try:
135
- raise EmailerNotSetupError("Setup has not been run, sending or displaying an email cannot occur.")
136
- except EmailerNotSetupError as e:
137
- self._logger.error(e, exc_info=True)
138
- raise e
139
-
140
-
141
- if __name__ == "__main__":
142
- module_name = __file__.split('\\')[-1].split('.py')[0]
143
-
144
- """emailer = PyEmailer(display_window=False, send_emails=True, auto_send=False)
145
-
146
- r_dict = {
147
- "subject": f"TEST: Your TEST "
148
- f"agreement expires in 30 days or less!",
149
- "text": f"To whom it may concern,<br>"
150
- f"&emsp; Your TEST "
151
- f"agreement <b>expires in 30 days or less (your due date is TEST)</b>. "
152
- f"Please renew the aforementioned by dropping off a hard copy to "
153
- f"the Albany Water Department at 10 N Enterprise Dr.<br>"
154
- f"Thank You,<br>"
155
- f"AWD"
156
- f"<br>"
157
- f"<br>"
158
- f"<br>"
159
- f"This is an automated email, Please do not respond directly to it.",
160
- "recipient": ''
161
- }
162
- # &emsp; is the tab character for emails
163
- emailer.SetupEmail(**r_dict) # recipient="amcsparron@albanyny.gov", subject="test subject", text="test_body")
164
- emailer.SendOrDisplay()"""
@@ -1,8 +0,0 @@
1
- # <u>PyEmailer</u>
2
- ### <i>Makes automating Outlook emails quick and easy</i>
3
-
4
- - PyEmailer uses pywin32 to automate the sending of Outlook emails.
5
-
6
- <b>Import</b>
7
-
8
- import SQLite3HelperClass using `from PyEmailer import PyEmailer`
File without changes
File without changes