-
Notifications
You must be signed in to change notification settings - Fork 1
/
quemail.py
164 lines (144 loc) · 5.46 KB
/
quemail.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
Simple implementation of mailer based on thread and queues. Early version.
@author: Marcin Chwalek <[email protected]>
Example of use:
# at start
qm = QueMail.get_instance()
qm.init('smtp.host.com', '[email protected]', 'SecretPassword')
qm.start()
...
# someware in app method
qm = QueMail.get_instance()
qm.send(Email(subject="Subject", text="Keep smiling :)", adr_to="[email protected]", adr_from="[email protected]"))
...
# after everything, at end of app
qm.end()
'''
import smtplib
import logging
import time
from email.mime.text import MIMEText
from email.utils import make_msgid, formatdate
from time import sleep
from queue import Queue
from threading import Thread
log = logging.getLogger("QueMail")
class QueMail(Thread):
instance = None
def init(self, smtp_host, smtp_login, smtp_pswd,
smtp_port = 25, use_tls = False, queue_size = 100, interval=5):
self._queue = Queue(queue_size)
log.info("Initializing QueMail (queue size = %i). "
"Using SMTP server: %s:%i %s TLS." % (
queue_size, smtp_host, smtp_port,
'with' if use_tls else 'without'))
self.smtp_host = smtp_host
self.smtp_login = smtp_login
self.smtp_password = smtp_pswd
self.smtp_port = smtp_port
self.use_tls = use_tls
self.check_interval = interval
try:
smtp = self.establish_SMTP_connection()
except Exception as e:
log.error('Error establishing connection to SMTP server: {}'.format(
e))
def __init__(self, interval=5):
Thread.__init__(self)
self.daemon = True
self._do_quit = False
self.setName("QueMail")
self.smtp_host = None
self.smtp_login = None
self.smtp_password = None
self.smtp_port = None
self.use_tls = False
self.check_interval = interval # number of seconds between queue checks
def end(self):
'''
Waits until all emails will be sent and after that stops thread
'''
log.info("Stopping QueMail thread...")
self._do_quit = True
self.join()
log.info("Stopped.")
def run(self):
while not self._do_quit:
if not self._queue.empty():
smtp = None
try:
smtp = self.establish_SMTP_connection()
while not self._queue.empty():
t = time.time()
eml = self._queue.get()
log.info(u"Sending (qs=%i): %s" % (self._queue.qsize(), eml))
try:
msg = eml.as_rfc_message()
content = msg.as_string()
log.debug(u"with content: %s" % content)
smtp.sendmail(eml.adr_from, eml.adr_to, content)
log.warning(u"Sent (qs=%i,t=%f): %s" % (self._queue.qsize(),time.time()-t, eml))
except Exception as e:
log.error(u"Exception occured while sending email: %s" % eml)
log.exception(e)
# FIXME not good idea: when exception occured, add email at end of queue
self._queue.put(eml, False)
sleep(1)
except Exception as e:
log.exception(e)
finally:
if smtp:
smtp.quit()
sleep(self.check_interval)
def establish_SMTP_connection(self):
log.debug(u"Connecting to SMTP server: %s:%i%s using TLS" % (
self.smtp_host, self.smtp_port, '' if self.use_tls else ' not'))
smtp = smtplib.SMTP(self.smtp_host, port=self.smtp_port)
if self.use_tls:
smtp.starttls()
smtp.ehlo()
else:
smtp.connect()
smtp.login(self.smtp_login, self.smtp_password)
return smtp
def send(self, eml):
if self._queue is None:
return
self._queue.put(eml, True, 5);
log.debug(u'Accepted (qs=%i): %s' % (self._queue.qsize(), eml))
@classmethod
def get_instance(cls):
if not cls.instance:
cls.instance = QueMail()
return cls.instance
class Email(object):
unique = 'unique-send'
def __init__(self, **props):
'''
@param adr_to: send to
@param adr_from: send from
@param subject: subject of email
@param mime_type: plain or html - only minor mime type of 'text/*'
@param text: text content of email
'''
self.text = props.get('text', '')
self.subject = props.get('subject', None)
self.adr_to = props.get('adr_to', None)
self.adr_from = props.get('adr_from', None)
self.mime_type = props.get('mime_type', 'plain')
def __str__(self):
return "Email to: %s, from: %s, sub: %s" % (self.adr_to, self.adr_from, self.subject)
def as_rfc_message(self):
'''
Creates standardized email with valid header
'''
msg = MIMEText(self.text, self.mime_type, 'utf-8')
msg['Subject'] = self.subject
msg['From'] = self.adr_from
msg['To'] = self.adr_to
msg['Date'] = formatdate()
msg['Reply-To'] = self.adr_from
msg['Message-Id'] = make_msgid(Email.unique)
return msg