Skip to content
This repository was archived by the owner on Nov 30, 2022. It is now read-only.

Commit 2f5727f

Browse files
authored
Merge pull request #101 from PatelKeviin/email-client-issue94
Added python email client script
2 parents bc5cce3 + 56aead0 commit 2f5727f

File tree

5 files changed

+315
-0
lines changed

5 files changed

+315
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Email Client using Python for automating email shooting process
2+
Python email client that can be used to automate email sending process.
3+
4+
## Pre-requisites :rotating_light:
5+
[![GitHub top language](https://img.shields.io/github/languages/top/vinitshahdeo/PortScanner?logo=python&logoColor=white)](https://www.python.org/)
6+
- **Python** `>= v3.7.x`
7+
- Install Python from [here](https://www.python.org/).
8+
- **Pip** `>= v20.0.x`
9+
- Install pip from [here](https://pip.pypa.io/en/stable/installing/).
10+
11+
## How to run? :rocket:
12+
### To run Email Client: (Via Terminal)
13+
- Update `email_client.py` file to enter Email credentials for the sender account as well as the recipient's address.
14+
- **Open terminal** and type **`python Email_CLient/email_client.py`**.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import os
2+
import smtplib
3+
from email.mime.multipart import MIMEMultipart
4+
from email.mime.text import MIMEText
5+
from email.mime.base import MIMEBase
6+
from email.utils import formatdate
7+
from email import encoders
8+
from pathlib import Path
9+
10+
11+
class EmailClient:
12+
""" An email client to send emails.
13+
"""
14+
15+
def __init__(self, server: str, server_port: int, user_name: str, password: str):
16+
""" Class constructor to initialise EmailClient
17+
18+
Parameters:
19+
server (str): Email server used by the user
20+
server_port (int): Port number of the email server
21+
user_name (str): Email address
22+
password (str): Email password
23+
"""
24+
25+
# Email credentials
26+
self.user_name = user_name
27+
self.__password = password
28+
29+
# used to add plain texts/HTML parts in the email
30+
self.__email_content = MIMEMultipart()
31+
# add sender's info
32+
self.__email_content['From'] = user_name
33+
# uses SMTP email protocol to communicate with email service provider
34+
self.__mail_server = smtplib.SMTP(
35+
host=server, port=server_port)
36+
37+
# necessary for internal workings
38+
self.__is_subject_added = False
39+
self.__is_body_added = False
40+
self.__is_attached = False
41+
self.__is_signature_added = False
42+
43+
def set_subject(self, subject: str):
44+
""" Method to set subject for the email (optional).
45+
46+
Parameters:
47+
subject (str): Email subject to set
48+
"""
49+
50+
self.__is_subject_added = (subject is not None and subject != '')
51+
if self.__is_subject_added:
52+
self.__email_content['Subject'] = subject
53+
54+
def set_body(self, body: str):
55+
""" Method to set body for the email (optional).
56+
57+
Parameters:
58+
body (str): Email body to set
59+
"""
60+
61+
self.__is_body_added = (body is not None and body != '')
62+
if self.__is_body_added:
63+
self.__email_content.attach(MIMEText(body, 'plain'))
64+
65+
def set_signature(self, signature: str):
66+
""" Method to set signature for the email (optional).
67+
68+
Parameters:
69+
signature (str): Email signature to set
70+
"""
71+
72+
self.__is_signature_added = (signature is not None and signature != '')
73+
if self.__is_signature_added:
74+
self.__email_content.attach(MIMEText(signature, 'plain'))
75+
76+
def add_attachment(self, attachment_path: str):
77+
""" Method to attach attachments in the email (optional).
78+
79+
Parameters:
80+
attachment_path (str): Path of attachment
81+
"""
82+
83+
attachment = MIMEBase('application', "octet-stream")
84+
85+
with open(attachment_path, 'rb') as file:
86+
attachment.set_payload(file.read())
87+
88+
encoders.encode_base64(attachment)
89+
attachment.add_header('Content-Disposition',
90+
'attachment; filename="{}"'.format(Path(attachment_path).name))
91+
92+
self.__email_content.attach(attachment)
93+
94+
# added attachment
95+
self.__is_attached = True
96+
97+
def send(self, recipient: str) -> bool:
98+
""" Method to send email message.
99+
100+
Parameters:
101+
recipient (str): Recipient's email address
102+
103+
Returns:
104+
bool: Determines success of email being sent
105+
"""
106+
if self.__is_attached and not self.__is_subject_added:
107+
print('Error: Subject is empty. Please add a subject and send again.')
108+
return False
109+
110+
if not self.__is_subject_added and not self.__is_body_added and not self.__is_signature_added:
111+
print('Error: Cannot send empty email message. Please add at least one from subject, body or signature.')
112+
return False
113+
114+
self.__email_content['To'] = recipient
115+
self.__email_content['Date'] = formatdate(localtime=True)
116+
117+
try:
118+
self.__mail_server.starttls() # start a secure TLS connection
119+
# login with user credentials on email server
120+
self.__mail_server.login(self.user_name, self.__password)
121+
122+
# send email message
123+
self.__mail_server.send_message(self.__email_content)
124+
125+
return True
126+
except Exception as e:
127+
print('Something went wrong :(\n', e)
128+
129+
return False
130+
finally:
131+
# close connection with email server
132+
self.__mail_server.quit()
133+
134+
def reset_email(self):
135+
""" Resets all email content except for the initialisation details.
136+
"""
137+
138+
# used to add plain texts/HTML parts in the email
139+
self.__email_content = MIMEMultipart()
140+
# add sender's info
141+
self.__email_content['From'] = self.user_name
142+
143+
# necessary for internal workings
144+
self.__is_subject_added = False
145+
self.__is_body_added = False
146+
self.__is_attached = False
147+
self.__is_signature_added = False
148+
149+
150+
# driver code
151+
if __name__ == "__main__":
152+
153+
# NOTE: if you're using Gmail account for sending email, you may have to turn off one of Google account's setting.
154+
# link: https://myaccount.google.com/lesssecureapps
155+
# More about this issue: https://stackoverflow.com/questions/16512592/login-credentials-not-working-with-gmail-smtp
156+
157+
# using Google mail server
158+
mail_server = 'smtp.gmail.com'
159+
port_number = 587
160+
# sender's credentials
161+
username = '[email protected]' # enter your gmail address
162+
with open('password.txt', 'r') as pass_file:
163+
email_password = pass_file.read()
164+
# recipient's email address
165+
email_recipient = '[email protected]' # enter sender's email address
166+
167+
# Email message content
168+
# Email subject
169+
email_subject = 'Hello, world!'
170+
171+
# Email body
172+
email_body = 'Hello there, \n\nThis is my automated email. Please do not reply.'
173+
174+
# Email signature
175+
email_signature = '\n\nKind regards,\n{}'.format(username)
176+
177+
# using 'EmailClient' class for sending email messages
178+
email_client = EmailClient(
179+
mail_server, port_number, username, email_password)
180+
email_client.set_subject(email_subject)
181+
email_client.set_body(email_body)
182+
email_client.set_signature(email_signature)
183+
# email_client.add_attachment('file_to_attach.txt')
184+
185+
# sending email
186+
if email_client.send(email_recipient):
187+
print('Email sent.')
188+
else:
189+
print('Failed :(')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from email_client import EmailClient
2+
import unittest
3+
4+
5+
class EmailClientTest(unittest.TestCase):
6+
""" Tests for EmailClient class
7+
"""
8+
9+
# credentials
10+
# using Google mail server
11+
__server = 'smtp.gmail.com'
12+
__port_number = 587
13+
# sender's credentials
14+
__username = '[email protected]'
15+
with open('password.txt', 'r') as pass_file:
16+
__password = pass_file.read()
17+
# recipient's email addr
18+
__recipient = '[email protected]'
19+
20+
# test case #1 - testing sending functionality of EmailClient class
21+
def test_email_client_send(self):
22+
# Email message content
23+
# Email subject
24+
subject = 'Testing'
25+
# Email body
26+
body = 'Hi,\n\nI am Kevin. How are you?'
27+
# Email signature
28+
signature = '\n\nKind regards,\n{}'.format(self.__username)
29+
30+
# sending Email
31+
email_client = EmailClient(
32+
self.__server, self.__port_number, self.__username, self.__password)
33+
email_client.set_subject(subject)
34+
email_client.set_body(body)
35+
email_client.set_signature(signature)
36+
email_client.add_attachment('test.txt')
37+
38+
# testing
39+
sent = True
40+
self.assertEqual(email_client.send(self.__recipient), sent)
41+
42+
# test case #2 - EmailClient Should not send empty emails
43+
def test_empty_email(self):
44+
# Empty Email message content
45+
# Email subject
46+
subject = ''
47+
# Email body
48+
body = ''
49+
# Email signature
50+
signature = ''
51+
52+
# sending Email
53+
email_client = EmailClient(
54+
self.__server, self.__port_number, self.__username, self.__password)
55+
email_client.set_subject(subject)
56+
email_client.set_body(body)
57+
email_client.set_signature(signature)
58+
59+
# testing
60+
sent = False
61+
self.assertEqual(email_client.send(self.__recipient), sent)
62+
63+
# test case #3 - testing reset functionality in EmailClient class
64+
def test_email_client_reset(self):
65+
# Email message content
66+
# Email subject
67+
subject = 'Testing Reset Method'
68+
# Email body
69+
body = 'Email body has not been reset. Please check its functionality.'
70+
# Email signature
71+
signature = '\n\nKind regards,\n[email protected]'
72+
73+
# initialising Email
74+
email_client = EmailClient(
75+
self.__server, self.__port_number, self.__username, self.__password)
76+
email_client.set_subject(subject)
77+
email_client.set_body(body)
78+
email_client.set_signature(signature)
79+
email_client.add_attachment('test.txt')
80+
81+
# resetting email content
82+
email_client.reset_email()
83+
84+
# adding Email body
85+
new_body = 'Email body has been reset.'
86+
email_client.set_body(new_body)
87+
88+
# testing
89+
sent = True
90+
self.assertEqual(email_client.send(self.__recipient), sent)
91+
92+
# test case #4 - testing sending functionality when special case of attachment added but not subject
93+
def test_email_client_send_misc(self):
94+
# Email message content
95+
# Email subject
96+
subject = ''
97+
98+
# sending Email
99+
email_client = EmailClient(
100+
self.__server, self.__port_number, self.__username, self.__password)
101+
email_client.set_subject(subject)
102+
# add Email attachment
103+
email_client.add_attachment('test.txt')
104+
105+
# testing
106+
sent = False
107+
self.assertEqual(email_client.send(self.__recipient), sent)
108+
109+
110+
if __name__ == '__main__':
111+
unittest.main() # running tests on EmailClient
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<your password>

System-Automation-Scripts/Email_Client/test.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)